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

wc-template-functions.php ➔ wc_display_item_data()   C

Complexity

Conditions 16
Paths 36

Size

Total Lines 65
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 37
nc 36
nop 2
dl 0
loc 65
rs 5.9197
c 0
b 0
f 0

How to fix   Long Method    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
2
/**
3
 * WooCommerce Template
4
 *
5
 * Functions for the templating system.
6
 *
7
 * @author   WooThemes
8
 * @category Core
9
 * @package  WooCommerce/Functions
10
 * @version  2.5.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit; // Exit if accessed directly
15
}
16
17
/**
18
 * Handle redirects before content is output - hooked into template_redirect so is_page works.
19
 */
20
function wc_template_redirect() {
21
	global $wp_query, $wp;
22
23
	if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && wc_get_page_id( 'shop' ) == $_GET['page_id'] ) {
24
25
		// When default permalinks are enabled, redirect shop page to post type archive url
26
		wp_safe_redirect( get_post_type_archive_link( 'product' ) );
27
		exit;
28
29
	} elseif ( is_page( wc_get_page_id( 'checkout' ) ) && wc_get_page_id( 'checkout' ) !== wc_get_page_id( 'cart' ) && WC()->cart->is_empty() && empty( $wp->query_vars['order-pay'] ) && ! isset( $wp->query_vars['order-received'] ) ) {
30
31
		// When on the checkout with an empty cart, redirect to cart page
32
		wc_add_notice( __( 'Checkout is not available whilst your cart is empty.', 'woocommerce' ), 'notice' );
33
		wp_redirect( wc_get_page_permalink( 'cart' ) );
34
		exit;
35
36
	} elseif ( isset( $wp->query_vars['customer-logout'] ) ) {
37
38
		// Logout
39
		wp_redirect( str_replace( '&amp;', '&', wp_logout_url( wc_get_page_permalink( 'myaccount' ) ) ) );
40
		exit;
41
42
	} elseif ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) {
43
44
		// Redirect to the product page if we have a single product
45
		$product = wc_get_product( $wp_query->post );
46
47
		if ( $product && $product->is_visible() ) {
48
			wp_safe_redirect( get_permalink( $product->id ), 302 );
49
			exit;
50
		}
51
	} elseif ( is_add_payment_method_page() ) {
52
53
		// Ensure payment gateways are loaded early
54
		WC()->payment_gateways();
55
56
	} elseif ( is_checkout() ) {
57
58
		// Checkout pages handling
59
		// Buffer the checkout page
60
		ob_start();
61
62
		// Ensure gateways and shipping methods are loaded early
63
		WC()->payment_gateways();
64
		WC()->shipping();
65
66
	}
67
}
68
add_action( 'template_redirect', 'wc_template_redirect' );
69
70
/**
71
 * When loading sensitive checkout or account pages, send a HTTP header to limit rendering of pages to same origin iframes for security reasons.
72
 *
73
 * Can be disabled with: remove_action( 'template_redirect', 'wc_send_frame_options_header' );
74
 *
75
 * @since  2.3.10
76
 */
77
function wc_send_frame_options_header() {
78
	if ( is_checkout() || is_account_page() ) {
79
		send_frame_options_header();
80
	}
81
}
82
add_action( 'template_redirect', 'wc_send_frame_options_header' );
83
84
/**
85
 * No index our endpoints.
86
 * Prevent indexing pages like order-received.
87
 *
88
 * @since 2.5.3
89
 */
90
function wc_prevent_endpoint_indexing() {
91
	if ( is_wc_endpoint_url() || isset( $_GET['download_file'] ) ) {
92
		@header( 'X-Robots-Tag: noindex' );
93
	}
94
}
95
add_action( 'template_redirect', 'wc_prevent_endpoint_indexing' );
96
97
/**
98
 * Remove adjacent_posts_rel_link_wp_head - pointless for products.
99
 *
100
 * @since 2.7.0
101
 */
102
function wc_prevent_adjacent_posts_rel_link_wp_head() {
103
	if ( is_singular( 'product' ) ) {
104
		remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );
105
	}
106
}
107
add_action( 'template_redirect', 'wc_prevent_adjacent_posts_rel_link_wp_head' );
108
109
/**
110
 * When the_post is called, put product data into a global.
111
 *
112
 * @param mixed $post
113
 * @return WC_Product
114
 */
115
function wc_setup_product_data( $post ) {
116
	unset( $GLOBALS['product'] );
117
118
	if ( is_int( $post ) )
119
		$post = get_post( $post );
120
121
	if ( empty( $post->post_type ) || ! in_array( $post->post_type, array( 'product', 'product_variation' ) ) )
122
		return;
123
124
	$GLOBALS['product'] = wc_get_product( $post );
125
126
	return $GLOBALS['product'];
127
}
128
add_action( 'the_post', 'wc_setup_product_data' );
129
130
if ( ! function_exists( 'woocommerce_reset_loop' ) ) {
131
132
	/**
133
	 * Reset the loop's index and columns when we're done outputting a product loop.
134
	 * @subpackage	Loop
135
	 */
136
	function woocommerce_reset_loop() {
137
		$GLOBALS['woocommerce_loop'] = array(
138
			'loop'    => '',
139
			'columns' => '',
140
			'name'    => '',
141
		);
142
	}
143
}
144
add_filter( 'loop_end', 'woocommerce_reset_loop' );
145
146
/**
147
 * Products RSS Feed.
148
 * @deprecated 2.6
149
 * @access public
150
 */
151
function wc_products_rss_feed() {
152
	// Product RSS
153
	if ( is_post_type_archive( 'product' ) || is_singular( 'product' ) ) {
154
155
		$feed = get_post_type_archive_feed_link( 'product' );
156
157
		echo '<link rel="alternate" type="application/rss+xml"  title="' . esc_attr__( 'New products', 'woocommerce' ) . '" href="' . esc_url( $feed ) . '" />';
158
159
	} elseif ( is_tax( 'product_cat' ) ) {
160
161
		$term = get_term_by( 'slug', esc_attr( get_query_var( 'product_cat' ) ), 'product_cat' );
162
163 View Code Duplication
		if ( $term ) {
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...
164
			$feed = add_query_arg( 'product_cat', $term->slug, get_post_type_archive_feed_link( 'product' ) );
165
			echo '<link rel="alternate" type="application/rss+xml"  title="' . esc_attr( sprintf( __( 'New products added to %s', 'woocommerce' ), $term->name ) ) . '" href="' . esc_url( $feed ) . '" />';
166
		}
167
	} elseif ( is_tax( 'product_tag' ) ) {
168
169
		$term = get_term_by( 'slug', esc_attr( get_query_var( 'product_tag' ) ), 'product_tag' );
170
171 View Code Duplication
		if ( $term ) {
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...
172
			$feed = add_query_arg( 'product_tag', $term->slug, get_post_type_archive_feed_link( 'product' ) );
173
			echo '<link rel="alternate" type="application/rss+xml"  title="' . sprintf( __( 'New products tagged %s', 'woocommerce' ), urlencode( $term->name ) ) . '" href="' . esc_url( $feed ) . '" />';
174
		}
175
	}
176
}
177
178
/**
179
 * Output generator tag to aid debugging.
180
 *
181
 * @access public
182
 */
183
function wc_generator_tag( $gen, $type ) {
184
	switch ( $type ) {
185
		case 'html':
186
			$gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '">';
187
			break;
188
		case 'xhtml':
189
			$gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '" />';
190
			break;
191
	}
192
	return $gen;
193
}
194
195
/**
196
 * Add body classes for WC pages.
197
 *
198
 * @param  array $classes
199
 * @return array
200
 */
201
function wc_body_class( $classes ) {
202
	$classes = (array) $classes;
203
204
	if ( is_woocommerce() ) {
205
206
		$classes[] = 'woocommerce';
207
		$classes[] = 'woocommerce-page';
208
209
	} elseif ( is_checkout() ) {
210
211
		$classes[] = 'woocommerce-checkout';
212
		$classes[] = 'woocommerce-page';
213
214
	} elseif ( is_cart() ) {
215
216
		$classes[] = 'woocommerce-cart';
217
		$classes[] = 'woocommerce-page';
218
219
	} elseif ( is_account_page() ) {
220
221
		$classes[] = 'woocommerce-account';
222
		$classes[] = 'woocommerce-page';
223
224
	}
225
226
	if ( is_store_notice_showing() ) {
227
		$classes[] = 'woocommerce-demo-store';
228
	}
229
230
	foreach ( WC()->query->query_vars as $key => $value ) {
231
		if ( is_wc_endpoint_url( $key ) ) {
232
			$classes[] = 'woocommerce-' . sanitize_html_class( $key );
233
		}
234
	}
235
236
	return array_unique( $classes );
237
}
238
239
/**
240
 * Display the classes for the product cat div.
241
 *
242
 * @since 2.4.0
243
 * @param string|array $class One or more classes to add to the class list.
244
 * @param object $category object Optional.
245
 */
246
function wc_product_cat_class( $class = '', $category = null ) {
247
	// Separates classes with a single space, collates classes for post DIV
248
	echo 'class="' . esc_attr( join( ' ', wc_get_product_cat_class( $class, $category ) ) ) . '"';
249
}
250
251
/**
252
 * Get classname for loops based on $woocommerce_loop global.
253
 * @since 2.6.0
254
 * @return string
255
 */
256
function wc_get_loop_class() {
257
	global $woocommerce_loop;
258
259
	$woocommerce_loop['loop']    = ! empty( $woocommerce_loop['loop'] ) ? $woocommerce_loop['loop'] + 1   : 1;
260
	$woocommerce_loop['columns'] = ! empty( $woocommerce_loop['columns'] ) ? $woocommerce_loop['columns'] : apply_filters( 'loop_shop_columns', 4 );
261
262
	if ( 0 === ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 === $woocommerce_loop['columns'] ) {
263
		return 'first';
264
	} elseif ( 0 === $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) {
265
		return 'last';
266
	} else {
267
		return '';
268
	}
269
}
270
271
/**
272
 * Get the classes for the product cat div.
273
 *
274
 * @since 2.4.0
275
 * @param string|array $class One or more classes to add to the class list.
276
 * @param object $category object Optional.
277
 */
278
function wc_get_product_cat_class( $class = '', $category = null ) {
279
	$classes   = is_array( $class ) ? $class : array_map( 'trim', explode( ' ', $class ) );
280
	$classes[] = 'product-category';
281
	$classes[] = 'product';
282
	$classes[] = wc_get_loop_class();
283
	$classes   = apply_filters( 'product_cat_class', $classes, $class, $category );
284
285
	return array_unique( array_filter( $classes ) );
286
}
287
288
/**
289
 * Adds extra post classes for products.
290
 *
291
 * @since 2.1.0
292
 * @param array $classes
293
 * @param string|array $class
294
 * @param int $post_id
295
 * @return array
296
 */
297
function wc_product_post_class( $classes, $class = '', $post_id = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $class is not used and could be removed.

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

Loading history...
298
	if ( ! $post_id || 'product' !== get_post_type( $post_id ) ) {
299
		return $classes;
300
	}
301
302
	$product = wc_get_product( $post_id );
303
304
	if ( $product ) {
305
		$classes[] = wc_get_loop_class();
306
		$classes[] = $product->stock_status;
307
308
		if ( $product->is_on_sale() ) {
309
			$classes[] = 'sale';
310
		}
311
		if ( $product->is_featured() ) {
312
			$classes[] = 'featured';
313
		}
314
		if ( $product->is_downloadable() ) {
315
			$classes[] = 'downloadable';
316
		}
317
		if ( $product->is_virtual() ) {
318
			$classes[] = 'virtual';
319
		}
320
		if ( $product->is_sold_individually() ) {
321
			$classes[] = 'sold-individually';
322
		}
323
		if ( $product->is_taxable() ) {
324
			$classes[] = 'taxable';
325
		}
326
		if ( $product->is_shipping_taxable() ) {
327
			$classes[] = 'shipping-taxable';
328
		}
329
		if ( $product->is_purchasable() ) {
330
			$classes[] = 'purchasable';
331
		}
332
		if ( $product->get_type() ) {
333
			$classes[] = "product-type-" . $product->get_type();
334
		}
335
		if ( $product->is_type( 'variable' ) ) {
336
			if ( $product->has_default_attributes() ) {
337
				$classes[] = 'has-default-attributes';
338
			}
339
			if ( $product->has_child() ) {
340
				$classes[] = 'has-children';
341
			}
342
		}
343
	}
344
345
	if ( false !== ( $key = array_search( 'hentry', $classes ) ) ) {
346
		unset( $classes[ $key ] );
347
	}
348
349
	return $classes;
350
}
351
352
/**
353
 * Outputs hidden form inputs for each query string variable.
354
 * @since 2.7.0
355
 * @param array $values Name value pairs.
356
 * @param array $exclude Keys to exclude.
357
 * @param string $current_key Current key we are outputting.
358
 */
359
function wc_query_string_form_fields( $values = null, $exclude = array(), $current_key = '', $return = false ) {
360
	if ( is_null( $values ) ) {
361
		$values = $_GET;
362
	}
363
	$html = '';
364
365
	foreach ( $values as $key => $value ) {
366
		if ( in_array( $key, $exclude, true ) ) {
367
			continue;
368
		}
369
		if ( $current_key ) {
370
			$key = $current_key . '[' . $key . ']';
371
		}
372
		if ( is_array( $value ) ) {
373
			$html .= wc_query_string_form_fields( $value, $exclude, $key, true );
374
		} else {
375
			$html .= '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
376
		}
377
	}
378
379
	if ( $return ) {
380
		return $html;
381
	} else {
382
		echo $html;
383
	}
384
}
385
386
/** Template pages ********************************************************/
387
388
if ( ! function_exists( 'woocommerce_content' ) ) {
389
390
	/**
391
	 * Output WooCommerce content.
392
	 *
393
	 * This function is only used in the optional 'woocommerce.php' template.
394
	 * which people can add to their themes to add basic woocommerce support.
395
	 * without hooks or modifying core templates.
396
	 *
397
	 */
398
	function woocommerce_content() {
399
400
		if ( is_singular( 'product' ) ) {
401
402
			while ( have_posts() ) : the_post();
403
404
				wc_get_template_part( 'content', 'single-product' );
405
406
			endwhile;
407
408
		} else { ?>
409
410
			<?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
411
412
				<h1 class="page-title"><?php woocommerce_page_title(); ?></h1>
413
414
			<?php endif; ?>
415
416
			<?php do_action( 'woocommerce_archive_description' ); ?>
417
418 View Code Duplication
			<?php if ( have_posts() ) : ?>
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
420
				<?php do_action( 'woocommerce_before_shop_loop' ); ?>
421
422
				<?php woocommerce_product_loop_start(); ?>
423
424
					<?php woocommerce_product_subcategories(); ?>
425
426
					<?php while ( have_posts() ) : the_post(); ?>
427
428
						<?php wc_get_template_part( 'content', 'product' ); ?>
429
430
					<?php endwhile; // end of the loop. ?>
431
432
				<?php woocommerce_product_loop_end(); ?>
433
434
				<?php do_action( 'woocommerce_after_shop_loop' ); ?>
435
436
			<?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?>
437
438
				<?php do_action( 'woocommerce_no_products_found' ); ?>
439
440
			<?php endif;
441
442
		}
443
	}
444
}
445
446
/** Global ****************************************************************/
447
448
if ( ! function_exists( 'woocommerce_output_content_wrapper' ) ) {
449
450
	/**
451
	 * Output the start of the page wrapper.
452
	 *
453
	 */
454
	function woocommerce_output_content_wrapper() {
455
		wc_get_template( 'global/wrapper-start.php' );
456
	}
457
}
458
if ( ! function_exists( 'woocommerce_output_content_wrapper_end' ) ) {
459
460
	/**
461
	 * Output the end of the page wrapper.
462
	 *
463
	 */
464
	function woocommerce_output_content_wrapper_end() {
465
		wc_get_template( 'global/wrapper-end.php' );
466
	}
467
}
468
469
if ( ! function_exists( 'woocommerce_get_sidebar' ) ) {
470
471
	/**
472
	 * Get the shop sidebar template.
473
	 *
474
	 */
475
	function woocommerce_get_sidebar() {
476
		wc_get_template( 'global/sidebar.php' );
477
	}
478
}
479
480
if ( ! function_exists( 'woocommerce_demo_store' ) ) {
481
482
	/**
483
	 * Adds a demo store banner to the site if enabled.
484
	 *
485
	 */
486
	function woocommerce_demo_store() {
487
		if ( ! is_store_notice_showing() ) {
488
			return;
489
		}
490
491
		$notice = get_option( 'woocommerce_demo_store_notice' );
492
493
		if ( empty( $notice ) ) {
494
			$notice = __( 'This is a demo store for testing purposes &mdash; no orders shall be fulfilled.', 'woocommerce' );
495
		}
496
497
		echo apply_filters( 'woocommerce_demo_store', '<p class="demo_store">' . wp_kses_post( $notice ) . '</p>', $notice );
498
	}
499
}
500
501
/** Loop ******************************************************************/
502
503
if ( ! function_exists( 'woocommerce_page_title' ) ) {
504
505
	/**
506
	 * woocommerce_page_title function.
507
	 *
508
	 * @param  bool $echo
509
	 * @return string
510
	 */
511
	function woocommerce_page_title( $echo = true ) {
512
513
		if ( is_search() ) {
514
			$page_title = sprintf( __( 'Search Results: &ldquo;%s&rdquo;', 'woocommerce' ), get_search_query() );
515
516
			if ( get_query_var( 'paged' ) )
517
				$page_title .= sprintf( __( '&nbsp;&ndash; Page %s', 'woocommerce' ), get_query_var( 'paged' ) );
518
519
		} elseif ( is_tax() ) {
520
521
			$page_title = single_term_title( "", false );
522
523
		} else {
524
525
			$shop_page_id = wc_get_page_id( 'shop' );
526
			$page_title   = get_the_title( $shop_page_id );
527
528
		}
529
530
		$page_title = apply_filters( 'woocommerce_page_title', $page_title );
531
532
		if ( $echo )
533
			echo $page_title;
534
		else
535
			return $page_title;
536
	}
537
}
538
539
if ( ! function_exists( 'woocommerce_product_loop_start' ) ) {
540
541
	/**
542
	 * Output the start of a product loop. By default this is a UL.
543
	 *
544
	 * @param bool $echo
545
	 * @return string
546
	 */
547
	function woocommerce_product_loop_start( $echo = true ) {
548
		ob_start();
549
		$GLOBALS['woocommerce_loop']['loop'] = 0;
550
		wc_get_template( 'loop/loop-start.php' );
551
		if ( $echo )
552
			echo ob_get_clean();
553
		else
554
			return ob_get_clean();
555
	}
556
}
557
if ( ! function_exists( 'woocommerce_product_loop_end' ) ) {
558
559
	/**
560
	 * Output the end of a product loop. By default this is a UL.
561
	 *
562
	 * @param bool $echo
563
	 * @return string
564
	 */
565
	function woocommerce_product_loop_end( $echo = true ) {
566
		ob_start();
567
568
		wc_get_template( 'loop/loop-end.php' );
569
570
		if ( $echo )
571
			echo ob_get_clean();
572
		else
573
			return ob_get_clean();
574
	}
575
}
576
if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) {
577
578
	/**
579
	 * Show the product title in the product loop. By default this is an H3.
580
	 */
581
	function woocommerce_template_loop_product_title() {
582
		$tag = is_product_taxonomy() || is_shop() ? 'h2' : 'h3';
583
584
		echo '<' . $tag . ' class="woocommerce-loop-product__title">' . get_the_title() . '</' . $tag . '>';
585
	}
586
}
587
if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) {
588
589
	/**
590
	 * Show the subcategory title in the product loop.
591
	 */
592
	function woocommerce_template_loop_category_title( $category ) {
593
		?>
594
		<h2 class="woocommerce-loop-category__title">
595
			<?php
596
				echo $category->name;
597
598
				if ( $category->count > 0 )
599
					echo apply_filters( 'woocommerce_subcategory_count_html', ' <mark class="count">(' . $category->count . ')</mark>', $category );
600
			?>
601
		</h2>
602
		<?php
603
	}
604
}
605
/**
606
 * Insert the opening anchor tag for products in the loop.
607
 */
608
function woocommerce_template_loop_product_link_open() {
609
	echo '<a href="' . get_the_permalink() . '" class="woocommerce-LoopProduct-link">';
610
}
611
/**
612
 * Insert the opening anchor tag for products in the loop.
613
 */
614
function woocommerce_template_loop_product_link_close() {
615
	echo '</a>';
616
}
617
/**
618
 * Insert the opening anchor tag for categories in the loop.
619
 */
620
function woocommerce_template_loop_category_link_open( $category ) {
621
	echo '<a href="' . get_term_link( $category, 'product_cat' ) . '">';
622
}
623
/**
624
 * Insert the opening anchor tag for categories in the loop.
625
 */
626
function woocommerce_template_loop_category_link_close() {
627
	echo '</a>';
628
}
629
if ( ! function_exists( 'woocommerce_taxonomy_archive_description' ) ) {
630
631
	/**
632
	 * Show an archive description on taxonomy archives.
633
	 *
634
	 * @subpackage	Archives
635
	 */
636
	function woocommerce_taxonomy_archive_description() {
637
		if ( is_product_taxonomy() && 0 === absint( get_query_var( 'paged' ) ) ) {
638
			$description = wc_format_content( term_description() );
639
			if ( $description ) {
640
				echo '<div class="term-description">' . $description . '</div>';
641
			}
642
		}
643
	}
644
}
645
if ( ! function_exists( 'woocommerce_product_archive_description' ) ) {
646
647
	/**
648
	 * Show a shop page description on product archives.
649
	 *
650
	 * @subpackage	Archives
651
	 */
652
	function woocommerce_product_archive_description() {
653
		// Don't display the description on search results page
654
		if ( is_search() ) {
655
			return;
656
		}
657
658
		if ( is_post_type_archive( 'product' ) && 0 === absint( get_query_var( 'paged' ) ) ) {
659
			$shop_page   = get_post( wc_get_page_id( 'shop' ) );
660
			if ( $shop_page ) {
661
				$description = wc_format_content( $shop_page->post_content );
662
				if ( $description ) {
663
					echo '<div class="page-description">' . $description . '</div>';
664
				}
665
			}
666
		}
667
	}
668
}
669
670
if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) {
671
672
	/**
673
	 * Get the add to cart template for the loop.
674
	 *
675
	 * @subpackage	Loop
676
	 */
677
	function woocommerce_template_loop_add_to_cart( $args = array() ) {
678
		global $product;
679
680
		if ( $product ) {
681
			$defaults = array(
682
				'quantity' => 1,
683
				'class'    => implode( ' ', array_filter( array(
684
						'button',
685
						'product_type_' . $product->product_type,
686
						$product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
687
						$product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : '',
688
				) ) ),
689
			);
690
691
			$args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product );
692
693
			wc_get_template( 'loop/add-to-cart.php', $args );
694
		}
695
	}
696
}
697
if ( ! function_exists( 'woocommerce_template_loop_product_thumbnail' ) ) {
698
699
	/**
700
	 * Get the product thumbnail for the loop.
701
	 *
702
	 * @subpackage	Loop
703
	 */
704
	function woocommerce_template_loop_product_thumbnail() {
705
		echo woocommerce_get_product_thumbnail();
706
	}
707
}
708
if ( ! function_exists( 'woocommerce_template_loop_price' ) ) {
709
710
	/**
711
	 * Get the product price for the loop.
712
	 *
713
	 * @subpackage	Loop
714
	 */
715
	function woocommerce_template_loop_price() {
716
		wc_get_template( 'loop/price.php' );
717
	}
718
}
719
if ( ! function_exists( 'woocommerce_template_loop_rating' ) ) {
720
721
	/**
722
	 * Display the average rating in the loop.
723
	 *
724
	 * @subpackage	Loop
725
	 */
726
	function woocommerce_template_loop_rating() {
727
		wc_get_template( 'loop/rating.php' );
728
	}
729
}
730
if ( ! function_exists( 'woocommerce_show_product_loop_sale_flash' ) ) {
731
732
	/**
733
	 * Get the sale flash for the loop.
734
	 *
735
	 * @subpackage	Loop
736
	 */
737
	function woocommerce_show_product_loop_sale_flash() {
738
		wc_get_template( 'loop/sale-flash.php' );
739
	}
740
}
741
742
if ( ! function_exists( 'woocommerce_get_product_schema' ) ) {
743
744
	/**
745
	 * Get a products Schema.
746
	 * @return string
747
	 */
748
	function woocommerce_get_product_schema() {
749
		global $product;
750
751
		$schema = "Product";
752
753
		// Downloadable product schema handling
754
		if ( $product->is_downloadable() ) {
755
			switch ( $product->download_type ) {
756
				case 'application' :
757
					$schema = "SoftwareApplication";
758
				break;
759
				case 'music' :
760
					$schema = "MusicAlbum";
761
				break;
762
				default :
763
					$schema = "Product";
764
				break;
765
			}
766
		}
767
768
		return 'http://schema.org/' . $schema;
769
	}
770
}
771
772
if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) {
773
774
	/**
775
	 * Get the product thumbnail, or the placeholder if not set.
776
	 *
777
	 * @subpackage	Loop
778
	 * @param string $size (default: 'shop_catalog')
779
	 * @param int $deprecated1 Deprecated since WooCommerce 2.0 (default: 0)
780
	 * @param int $deprecated2 Deprecated since WooCommerce 2.0 (default: 0)
781
	 * @return string
782
	 */
783
	function woocommerce_get_product_thumbnail( $size = 'shop_catalog', $deprecated1 = 0, $deprecated2 = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated1 is not used and could be removed.

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

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

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

Loading history...
784
		global $post;
785
		$image_size = apply_filters( 'single_product_archive_thumbnail_size', $size );
786
787
		if ( has_post_thumbnail() ) {
788
			$props = wc_get_product_attachment_props( get_post_thumbnail_id(), $post );
789
			return get_the_post_thumbnail( $post->ID, $image_size, array(
790
				'title'	 => $props['title'],
791
				'alt'    => $props['alt'],
792
			) );
793
		} elseif ( wc_placeholder_img_src() ) {
794
			return wc_placeholder_img( $image_size );
795
		}
796
	}
797
}
798
799
if ( ! function_exists( 'woocommerce_result_count' ) ) {
800
801
	/**
802
	 * Output the result count text (Showing x - x of x results).
803
	 *
804
	 * @subpackage	Loop
805
	 */
806
	function woocommerce_result_count() {
807
		wc_get_template( 'loop/result-count.php' );
808
	}
809
}
810
811
if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
812
813
	/**
814
	 * Output the product sorting options.
815
	 *
816
	 * @subpackage	Loop
817
	 */
818
	function woocommerce_catalog_ordering() {
819
		global $wp_query;
820
821
		if ( 1 === $wp_query->found_posts || ! woocommerce_products_will_display() ) {
822
			return;
823
		}
824
825
		$orderby                 = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
826
		$show_default_orderby    = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
827
		$catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array(
828
			'menu_order' => __( 'Default sorting', 'woocommerce' ),
829
			'popularity' => __( 'Sort by popularity', 'woocommerce' ),
830
			'rating'     => __( 'Sort by average rating', 'woocommerce' ),
831
			'date'       => __( 'Sort by newness', 'woocommerce' ),
832
			'price'      => __( 'Sort by price: low to high', 'woocommerce' ),
833
			'price-desc' => __( 'Sort by price: high to low', 'woocommerce' ),
834
		) );
835
836
		if ( ! $show_default_orderby ) {
837
			unset( $catalog_orderby_options['menu_order'] );
838
		}
839
840
		if ( 'no' === get_option( 'woocommerce_enable_review_rating' ) ) {
841
			unset( $catalog_orderby_options['rating'] );
842
		}
843
844
		wc_get_template( 'loop/orderby.php', array( 'catalog_orderby_options' => $catalog_orderby_options, 'orderby' => $orderby, 'show_default_orderby' => $show_default_orderby ) );
845
	}
846
}
847
848
if ( ! function_exists( 'woocommerce_pagination' ) ) {
849
850
	/**
851
	 * Output the pagination.
852
	 *
853
	 * @subpackage	Loop
854
	 */
855
	function woocommerce_pagination() {
856
		wc_get_template( 'loop/pagination.php' );
857
	}
858
}
859
860
/** Single Product ********************************************************/
861
862
if ( ! function_exists( 'woocommerce_show_product_images' ) ) {
863
864
	/**
865
	 * Output the product image before the single product summary.
866
	 *
867
	 * @subpackage	Product
868
	 */
869
	function woocommerce_show_product_images() {
870
		wc_get_template( 'single-product/product-image.php' );
871
	}
872
}
873
if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
874
875
	/**
876
	 * Output the product thumbnails.
877
	 *
878
	 * @subpackage	Product
879
	 */
880
	function woocommerce_show_product_thumbnails() {
881
		wc_get_template( 'single-product/product-thumbnails.php' );
882
	}
883
}
884
if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) {
885
886
	/**
887
	 * Output the product tabs.
888
	 *
889
	 * @subpackage	Product/Tabs
890
	 */
891
	function woocommerce_output_product_data_tabs() {
892
		wc_get_template( 'single-product/tabs/tabs.php' );
893
	}
894
}
895
if ( ! function_exists( 'woocommerce_template_single_title' ) ) {
896
897
	/**
898
	 * Output the product title.
899
	 *
900
	 * @subpackage	Product
901
	 */
902
	function woocommerce_template_single_title() {
903
		wc_get_template( 'single-product/title.php' );
904
	}
905
}
906
if ( ! function_exists( 'woocommerce_template_single_rating' ) ) {
907
908
	/**
909
	 * Output the product rating.
910
	 *
911
	 * @subpackage	Product
912
	 */
913
	function woocommerce_template_single_rating() {
914
		wc_get_template( 'single-product/rating.php' );
915
	}
916
}
917
if ( ! function_exists( 'woocommerce_template_single_price' ) ) {
918
919
	/**
920
	 * Output the product price.
921
	 *
922
	 * @subpackage	Product
923
	 */
924
	function woocommerce_template_single_price() {
925
		wc_get_template( 'single-product/price.php' );
926
	}
927
}
928
if ( ! function_exists( 'woocommerce_template_single_excerpt' ) ) {
929
930
	/**
931
	 * Output the product short description (excerpt).
932
	 *
933
	 * @subpackage	Product
934
	 */
935
	function woocommerce_template_single_excerpt() {
936
		wc_get_template( 'single-product/short-description.php' );
937
	}
938
}
939
if ( ! function_exists( 'woocommerce_template_single_meta' ) ) {
940
941
	/**
942
	 * Output the product meta.
943
	 *
944
	 * @subpackage	Product
945
	 */
946
	function woocommerce_template_single_meta() {
947
		wc_get_template( 'single-product/meta.php' );
948
	}
949
}
950
if ( ! function_exists( 'woocommerce_template_single_sharing' ) ) {
951
952
	/**
953
	 * Output the product sharing.
954
	 *
955
	 * @subpackage	Product
956
	 */
957
	function woocommerce_template_single_sharing() {
958
		wc_get_template( 'single-product/share.php' );
959
	}
960
}
961
if ( ! function_exists( 'woocommerce_show_product_sale_flash' ) ) {
962
963
	/**
964
	 * Output the product sale flash.
965
	 *
966
	 * @subpackage	Product
967
	 */
968
	function woocommerce_show_product_sale_flash() {
969
		wc_get_template( 'single-product/sale-flash.php' );
970
	}
971
}
972
973
if ( ! function_exists( 'woocommerce_template_single_add_to_cart' ) ) {
974
975
	/**
976
	 * Trigger the single product add to cart action.
977
	 *
978
	 * @subpackage	Product
979
	 */
980
	function woocommerce_template_single_add_to_cart() {
981
		global $product;
982
		do_action( 'woocommerce_' . $product->product_type . '_add_to_cart' );
983
	}
984
}
985
if ( ! function_exists( 'woocommerce_simple_add_to_cart' ) ) {
986
987
	/**
988
	 * Output the simple product add to cart area.
989
	 *
990
	 * @subpackage	Product
991
	 */
992
	function woocommerce_simple_add_to_cart() {
993
		wc_get_template( 'single-product/add-to-cart/simple.php' );
994
	}
995
}
996
if ( ! function_exists( 'woocommerce_grouped_add_to_cart' ) ) {
997
998
	/**
999
	 * Output the grouped product add to cart area.
1000
	 *
1001
	 * @subpackage	Product
1002
	 */
1003
	function woocommerce_grouped_add_to_cart() {
1004
		global $product;
1005
1006
		wc_get_template( 'single-product/add-to-cart/grouped.php', array(
1007
			'grouped_product'    => $product,
1008
			'grouped_products'   => $product->get_children(),
1009
			'quantites_required' => false,
1010
		) );
1011
	}
1012
}
1013
if ( ! function_exists( 'woocommerce_variable_add_to_cart' ) ) {
1014
1015
	/**
1016
	 * Output the variable product add to cart area.
1017
	 *
1018
	 * @subpackage	Product
1019
	 */
1020
	function woocommerce_variable_add_to_cart() {
1021
		global $product;
1022
1023
		// Enqueue variation scripts
1024
		wp_enqueue_script( 'wc-add-to-cart-variation' );
1025
1026
		// Get Available variations?
1027
		$get_variations = sizeof( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product );
1028
1029
		// Load the template
1030
		wc_get_template( 'single-product/add-to-cart/variable.php', array(
1031
			'available_variations' => $get_variations ? $product->get_available_variations() : false,
1032
			'attributes'           => $product->get_variation_attributes(),
1033
			'selected_attributes'  => $product->get_variation_default_attributes(),
1034
		) );
1035
	}
1036
}
1037
if ( ! function_exists( 'woocommerce_external_add_to_cart' ) ) {
1038
1039
	/**
1040
	 * Output the external product add to cart area.
1041
	 *
1042
	 * @subpackage	Product
1043
	 */
1044
	function woocommerce_external_add_to_cart() {
1045
		global $product;
1046
1047
		if ( ! $product->add_to_cart_url() ) {
1048
			return;
1049
		}
1050
1051
		wc_get_template( 'single-product/add-to-cart/external.php', array(
1052
			'product_url' => $product->add_to_cart_url(),
1053
			'button_text' => $product->single_add_to_cart_text(),
1054
		) );
1055
	}
1056
}
1057
1058
if ( ! function_exists( 'woocommerce_quantity_input' ) ) {
1059
1060
	/**
1061
	 * Output the quantity input for add to cart forms.
1062
	 *
1063
	 * @param  array $args Args for the input
1064
	 * @param  WC_Product|null $product
1065
	 * @param  boolean $echo Whether to return or echo|string
1066
	 */
1067
	function woocommerce_quantity_input( $args = array(), $product = null, $echo = true ) {
1068
		if ( is_null( $product ) ) {
1069
			$product = $GLOBALS['product'];
1070
		}
1071
1072
		$defaults = array(
1073
			'input_name'  => 'quantity',
1074
			'input_value' => '1',
1075
			'max_value'   => apply_filters( 'woocommerce_quantity_input_max', '', $product ),
1076
			'min_value'   => apply_filters( 'woocommerce_quantity_input_min', '', $product ),
1077
			'step'        => apply_filters( 'woocommerce_quantity_input_step', '1', $product ),
1078
			'pattern'     => apply_filters( 'woocommerce_quantity_input_pattern', has_filter( 'woocommerce_stock_amount', 'intval' ) ? '[0-9]*' : '' ),
1079
			'inputmode'   => apply_filters( 'woocommerce_quantity_input_inputmode', has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'numeric' : '' ),
1080
		);
1081
1082
		$args = apply_filters( 'woocommerce_quantity_input_args', wp_parse_args( $args, $defaults ), $product );
1083
1084
		// Set min and max value to empty string if not set.
1085
		$args['min_value'] = isset( $args['min_value'] ) ? $args['min_value'] : '';
1086
		$args['max_value'] = isset( $args['max_value'] ) ? $args['max_value'] : '';
1087
1088
		// Apply sanity to min/max args - min cannot be lower than 0
1089
		if ( '' !== $args['min_value'] && is_numeric( $args['min_value'] ) && $args['min_value'] < 0 ) {
1090
			$args['min_value'] = 0; // Cannot be lower than 0
1091
		}
1092
1093
		// Max cannot be lower than 0 or min
1094
		if ( '' !== $args['max_value'] && is_numeric( $args['max_value'] ) ) {
1095
			$args['max_value'] = $args['max_value'] < 0 ? 0 : $args['max_value'];
1096
			$args['max_value'] = $args['max_value'] < $args['min_value'] ? $args['min_value'] : $args['max_value'];
1097
		}
1098
1099
		ob_start();
1100
1101
		wc_get_template( 'global/quantity-input.php', $args );
1102
1103
		if ( $echo ) {
1104
			echo ob_get_clean();
1105
		} else {
1106
			return ob_get_clean();
1107
		}
1108
	}
1109
}
1110
1111
if ( ! function_exists( 'woocommerce_product_description_tab' ) ) {
1112
1113
	/**
1114
	 * Output the description tab content.
1115
	 *
1116
	 * @subpackage	Product/Tabs
1117
	 */
1118
	function woocommerce_product_description_tab() {
1119
		wc_get_template( 'single-product/tabs/description.php' );
1120
	}
1121
}
1122
if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {
1123
1124
	/**
1125
	 * Output the attributes tab content.
1126
	 *
1127
	 * @subpackage	Product/Tabs
1128
	 */
1129
	function woocommerce_product_additional_information_tab() {
1130
		wc_get_template( 'single-product/tabs/additional-information.php' );
1131
	}
1132
}
1133
if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
1134
1135
	/**
1136
	 * Output the reviews tab content.
1137
	 * @deprecated  2.4.0 Unused
1138
	 * @subpackage	Product/Tabs
1139
	 */
1140
	function woocommerce_product_reviews_tab() {
1141
		_deprecated_function( 'woocommerce_product_reviews_tab', '2.4', '' );
1142
	}
1143
}
1144
1145
if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
1146
1147
	/**
1148
	 * Add default product tabs to product pages.
1149
	 *
1150
	 * @param array $tabs
1151
	 * @return array
1152
	 */
1153
	function woocommerce_default_product_tabs( $tabs = array() ) {
1154
		global $product, $post;
1155
1156
		// Description tab - shows product content
1157
		if ( $post->post_content ) {
1158
			$tabs['description'] = array(
1159
				'title'    => __( 'Description', 'woocommerce' ),
1160
				'priority' => 10,
1161
				'callback' => 'woocommerce_product_description_tab',
1162
			);
1163
		}
1164
1165
		// Additional information tab - shows attributes
1166
		if ( $product && ( $product->has_attributes() || $product->enable_dimensions_display() ) ) {
1167
			$tabs['additional_information'] = array(
1168
				'title'    => __( 'Additional Information', 'woocommerce' ),
1169
				'priority' => 20,
1170
				'callback' => 'woocommerce_product_additional_information_tab',
1171
			);
1172
		}
1173
1174
		// Reviews tab - shows comments
1175
		if ( comments_open() ) {
1176
			$tabs['reviews'] = array(
1177
				'title'    => sprintf( __( 'Reviews (%d)', 'woocommerce' ), $product->get_review_count() ),
1178
				'priority' => 30,
1179
				'callback' => 'comments_template',
1180
			);
1181
		}
1182
1183
		return $tabs;
1184
	}
1185
}
1186
1187
if ( ! function_exists( 'woocommerce_sort_product_tabs' ) ) {
1188
1189
	/**
1190
	 * Sort tabs by priority.
1191
	 *
1192
	 * @param array $tabs
1193
	 * @return array
1194
	 */
1195
	function woocommerce_sort_product_tabs( $tabs = array() ) {
1196
1197
		// Make sure the $tabs parameter is an array
1198
		if ( ! is_array( $tabs ) ) {
1199
			trigger_error( "Function woocommerce_sort_product_tabs() expects an array as the first parameter. Defaulting to empty array." );
1200
			$tabs = array();
1201
		}
1202
1203
		// Re-order tabs by priority
1204
		if ( ! function_exists( '_sort_priority_callback' ) ) {
1205
			function _sort_priority_callback( $a, $b ) {
1206
				if ( $a['priority'] === $b['priority'] )
1207
					return 0;
1208
				return ( $a['priority'] < $b['priority'] ) ? -1 : 1;
1209
			}
1210
		}
1211
1212
		uasort( $tabs, '_sort_priority_callback' );
1213
1214
		return $tabs;
1215
	}
1216
}
1217
1218
if ( ! function_exists( 'woocommerce_comments' ) ) {
1219
1220
	/**
1221
	 * Output the Review comments template.
1222
	 *
1223
	 * @subpackage	Product
1224
	 * @param WP_Comment $comment
1225
	 * @param array $args
1226
	 * @param int $depth
1227
	 */
1228
	function woocommerce_comments( $comment, $args, $depth ) {
1229
		$GLOBALS['comment'] = $comment;
1230
		wc_get_template( 'single-product/review.php', array( 'comment' => $comment, 'args' => $args, 'depth' => $depth ) );
1231
	}
1232
}
1233
1234
if ( ! function_exists( 'woocommerce_review_display_gravatar' ) ) {
1235
	/**
1236
	 * Display the review authors gravatar
1237
	 *
1238
	 * @param array $comment WP_Comment.
1239
	 * @return void
1240
	 */
1241
	function woocommerce_review_display_gravatar( $comment ) {
1242
		echo get_avatar( $comment, apply_filters( 'woocommerce_review_gravatar_size', '60' ), '' );
1243
	}
1244
}
1245
1246
if ( ! function_exists( 'woocommerce_review_display_rating' ) ) {
1247
	/**
1248
	 * Display the reviewers star rating
1249
	 *
1250
	 * @return void
1251
	 */
1252
	function woocommerce_review_display_rating() {
1253
		wc_get_template( 'single-product/review-rating.php' );
1254
	}
1255
}
1256
1257
if ( ! function_exists( 'woocommerce_review_display_meta' ) ) {
1258
	/**
1259
	 * Display the review authors meta (name, verified owner, review date)
1260
	 *
1261
	 * @return void
1262
	 */
1263
	function woocommerce_review_display_meta() {
1264
		wc_get_template( 'single-product/review-meta.php' );
1265
	}
1266
}
1267
1268
if ( ! function_exists( 'woocommerce_review_display_comment_text' ) ) {
1269
1270
	/**
1271
	 * Display the review content.
1272
	 */
1273
	function woocommerce_review_display_comment_text() {
1274
		echo '<div itemprop="description" class="description">';
1275
		comment_text();
1276
		echo '</div>';
1277
	}
1278
}
1279
1280
if ( ! function_exists( 'woocommerce_output_related_products' ) ) {
1281
1282
	/**
1283
	 * Output the related products.
1284
	 *
1285
	 * @subpackage	Product
1286
	 */
1287
	function woocommerce_output_related_products() {
1288
1289
		$args = array(
1290
			'posts_per_page' 	=> 4,
1291
			'columns' 			=> 4,
1292
			'orderby' 			=> 'rand',
1293
		);
1294
1295
		woocommerce_related_products( apply_filters( 'woocommerce_output_related_products_args', $args ) );
1296
	}
1297
}
1298
1299
if ( ! function_exists( 'woocommerce_related_products' ) ) {
1300
1301
	/**
1302
	 * Output the related products.
1303
	 *
1304
	 * @param array Provided arguments
1305
	 * @param bool Columns argument for backwards compat
1306
	 * @param bool Order by argument for backwards compat
1307
	 */
1308
	function woocommerce_related_products( $args = array(), $columns = false, $orderby = false ) {
1309
		if ( ! is_array( $args ) ) {
1310
			_deprecated_argument( __FUNCTION__, '2.1', __( 'Use $args argument as an array instead. Deprecated argument will be removed in WC 2.2.', 'woocommerce' ) );
1311
1312
			$argsvalue = $args;
1313
1314
			$args = array(
1315
				'posts_per_page' => $argsvalue,
1316
				'columns'        => $columns,
1317
				'orderby'        => $orderby,
1318
			);
1319
		}
1320
1321
		$defaults = array(
1322
			'posts_per_page' => 2,
1323
			'columns'        => 2,
1324
			'orderby'        => 'rand',
1325
		);
1326
1327
		$args = wp_parse_args( $args, $defaults );
1328
1329
		wc_get_template( 'single-product/related.php', $args );
1330
	}
1331
}
1332
1333
if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
1334
1335
	/**
1336
	 * Output product up sells.
1337
	 *
1338
	 * @param int $posts_per_page (default: -1)
1339
	 * @param int $columns (default: 4)
1340
	 * @param string $orderby (default: 'rand')
1341
	 */
1342
	function woocommerce_upsell_display( $posts_per_page = '-1', $columns = 4, $orderby = 'rand' ) {
1343
		$args = apply_filters( 'woocommerce_upsell_display_args', array(
1344
			'posts_per_page'	=> $posts_per_page,
1345
			'orderby'			=> apply_filters( 'woocommerce_upsells_orderby', $orderby ),
1346
			'columns'			=> $columns,
1347
		) );
1348
1349
		wc_get_template( 'single-product/up-sells.php', $args );
1350
	}
1351
}
1352
1353
/** Cart ******************************************************************/
1354
1355
if ( ! function_exists( 'woocommerce_shipping_calculator' ) ) {
1356
1357
	/**
1358
	 * Output the cart shipping calculator.
1359
	 *
1360
	 * @subpackage	Cart
1361
	 */
1362
	function woocommerce_shipping_calculator() {
1363
		wc_get_template( 'cart/shipping-calculator.php' );
1364
	}
1365
}
1366
1367
if ( ! function_exists( 'woocommerce_cart_totals' ) ) {
1368
1369
	/**
1370
	 * Output the cart totals.
1371
	 *
1372
	 * @subpackage	Cart
1373
	 */
1374
	function woocommerce_cart_totals() {
1375
		if ( is_checkout() ) {
1376
			return;
1377
		}
1378
		wc_get_template( 'cart/cart-totals.php' );
1379
	}
1380
}
1381
1382
if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
1383
1384
	/**
1385
	 * Output the cart cross-sells.
1386
	 *
1387
	 * @param  int $posts_per_page (default: 2)
1388
	 * @param  int $columns (default: 2)
1389
	 * @param  string $orderby (default: 'rand')
1390
	 */
1391
	function woocommerce_cross_sell_display( $posts_per_page = 2, $columns = 2, $orderby = 'rand' ) {
1392
		if ( is_checkout() ) {
1393
			return;
1394
		}
1395
		wc_get_template( 'cart/cross-sells.php', array(
1396
			'posts_per_page' => $posts_per_page,
1397
			'orderby'        => $orderby,
1398
			'columns'        => $columns,
1399
		) );
1400
	}
1401
}
1402
1403
if ( ! function_exists( 'woocommerce_button_proceed_to_checkout' ) ) {
1404
1405
	/**
1406
	 * Output the proceed to checkout button.
1407
	 *
1408
	 * @subpackage	Cart
1409
	 */
1410
	function woocommerce_button_proceed_to_checkout() {
1411
		wc_get_template( 'cart/proceed-to-checkout-button.php' );
1412
	}
1413
}
1414
1415 View Code Duplication
if ( ! function_exists( 'woocommerce_widget_shopping_cart_button_view_cart' ) ) {
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...
1416
1417
	/**
1418
	 * Output the proceed to checkout button.
1419
	 *
1420
	 * @subpackage	Cart
1421
	 */
1422
	function woocommerce_widget_shopping_cart_button_view_cart() {
1423
		echo '<a href="' . esc_url( wc_get_cart_url() ) . '" class="button wc-forward">' . __( 'View Cart', 'woocommerce' ) . '</a>';
1424
	}
1425
}
1426
1427 View Code Duplication
if ( ! function_exists( 'woocommerce_widget_shopping_cart_proceed_to_checkout' ) ) {
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...
1428
1429
	/**
1430
	 * Output the proceed to checkout button.
1431
	 *
1432
	 * @subpackage	Cart
1433
	 */
1434
	function woocommerce_widget_shopping_cart_proceed_to_checkout() {
1435
		echo '<a href="' . esc_url( wc_get_checkout_url() ) . '" class="button checkout wc-forward">' . __( 'Checkout', 'woocommerce' ) . '</a>';
1436
	}
1437
}
1438
1439
/** Mini-Cart *************************************************************/
1440
1441
if ( ! function_exists( 'woocommerce_mini_cart' ) ) {
1442
1443
	/**
1444
	 * Output the Mini-cart - used by cart widget.
1445
	 *
1446
	 * @param array $args
1447
	 */
1448
	function woocommerce_mini_cart( $args = array() ) {
1449
1450
		$defaults = array(
1451
			'list_class' => '',
1452
		);
1453
1454
		$args = wp_parse_args( $args, $defaults );
1455
1456
		wc_get_template( 'cart/mini-cart.php', $args );
1457
	}
1458
}
1459
1460
/** Login *****************************************************************/
1461
1462
if ( ! function_exists( 'woocommerce_login_form' ) ) {
1463
1464
	/**
1465
	 * Output the WooCommerce Login Form.
1466
	 *
1467
	 * @subpackage	Forms
1468
	 * @param array $args
1469
	 */
1470
	function woocommerce_login_form( $args = array() ) {
1471
1472
		$defaults = array(
1473
			'message'  => '',
1474
			'redirect' => '',
1475
			'hidden'   => false,
1476
		);
1477
1478
		$args = wp_parse_args( $args, $defaults );
1479
1480
		wc_get_template( 'global/form-login.php', $args );
1481
	}
1482
}
1483
1484
if ( ! function_exists( 'woocommerce_checkout_login_form' ) ) {
1485
1486
	/**
1487
	 * Output the WooCommerce Checkout Login Form.
1488
	 *
1489
	 * @subpackage	Checkout
1490
	 */
1491
	function woocommerce_checkout_login_form() {
1492
		wc_get_template( 'checkout/form-login.php', array( 'checkout' => WC()->checkout() ) );
1493
	}
1494
}
1495
1496
if ( ! function_exists( 'woocommerce_breadcrumb' ) ) {
1497
1498
	/**
1499
	 * Output the WooCommerce Breadcrumb.
1500
	 *
1501
	 * @param array $args
1502
	 */
1503
	function woocommerce_breadcrumb( $args = array() ) {
1504
		$args = wp_parse_args( $args, apply_filters( 'woocommerce_breadcrumb_defaults', array(
1505
			'delimiter'   => '&nbsp;&#47;&nbsp;',
1506
			'wrap_before' => '<nav class="woocommerce-breadcrumb" ' . ( is_single() ? 'itemprop="breadcrumb"' : '' ) . '>',
1507
			'wrap_after'  => '</nav>',
1508
			'before'      => '',
1509
			'after'       => '',
1510
			'home'        => _x( 'Home', 'breadcrumb', 'woocommerce' ),
1511
		) ) );
1512
1513
		$breadcrumbs = new WC_Breadcrumb();
1514
1515
		if ( ! empty( $args['home'] ) ) {
1516
			$breadcrumbs->add_crumb( $args['home'], apply_filters( 'woocommerce_breadcrumb_home_url', home_url() ) );
1517
		}
1518
1519
		$args['breadcrumb'] = $breadcrumbs->generate();
1520
1521
		wc_get_template( 'global/breadcrumb.php', $args );
1522
	}
1523
}
1524
1525
if ( ! function_exists( 'woocommerce_order_review' ) ) {
1526
1527
	/**
1528
	 * Output the Order review table for the checkout.
1529
	 *
1530
	 * @subpackage	Checkout
1531
	 */
1532
	function woocommerce_order_review( $deprecated = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated is not used and could be removed.

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

Loading history...
1533
		wc_get_template( 'checkout/review-order.php', array( 'checkout' => WC()->checkout() ) );
1534
	}
1535
}
1536
1537
if ( ! function_exists( 'woocommerce_checkout_payment' ) ) {
1538
1539
	/**
1540
	 * Output the Payment Methods on the checkout.
1541
	 *
1542
	 * @subpackage	Checkout
1543
	 */
1544
	function woocommerce_checkout_payment() {
1545
		if ( WC()->cart->needs_payment() ) {
1546
			$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
1547
			WC()->payment_gateways()->set_current_gateway( $available_gateways );
1548
		} else {
1549
			$available_gateways = array();
1550
		}
1551
1552
		wc_get_template( 'checkout/payment.php', array(
1553
			'checkout'           => WC()->checkout(),
1554
			'available_gateways' => $available_gateways,
1555
			'order_button_text'  => apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ),
1556
		) );
1557
	}
1558
}
1559
1560
if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) {
1561
1562
	/**
1563
	 * Output the Coupon form for the checkout.
1564
	 *
1565
	 * @subpackage	Checkout
1566
	 */
1567
	function woocommerce_checkout_coupon_form() {
1568
		wc_get_template( 'checkout/form-coupon.php', array( 'checkout' => WC()->checkout() ) );
1569
	}
1570
}
1571
1572
if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
1573
1574
	/**
1575
	 * Check if we will be showing products or not (and not sub-categories only).
1576
	 * @subpackage	Loop
1577
	 * @return bool
1578
	 */
1579
	function woocommerce_products_will_display() {
1580
		global $wpdb;
1581
1582
		if ( is_shop() ) {
1583
			return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search();
1584
		}
1585
1586
		if ( ! is_product_taxonomy() ) {
1587
			return false;
1588
		}
1589
1590
		if ( is_search() || is_filtered() || is_paged() ) {
1591
			return true;
1592
		}
1593
1594
		$term = get_queried_object();
1595
1596
		if ( is_product_category() ) {
1597
			switch ( get_woocommerce_term_meta( $term->term_id, 'display_type', true ) ) {
1598
				case 'subcategories' :
1599
					// Nothing - we want to continue to see if there are products/subcats
1600
				break;
1601
				case 'products' :
1602
				case 'both' :
1603
					return true;
1604
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1605
				default :
1606
					// Default - no setting
1607
					if ( get_option( 'woocommerce_category_archive_display' ) != 'subcategories' ) {
1608
						return true;
1609
					}
1610
				break;
1611
			}
1612
		}
1613
1614
		// Begin subcategory logic
1615
		if ( empty( $term->term_id ) || empty( $term->taxonomy ) ) {
1616
			return true;
1617
		}
1618
1619
		$transient_name = 'wc_products_will_display_' . $term->term_id . '_' . WC_Cache_Helper::get_transient_version( 'product_query' );
1620
1621
		if ( false === ( $products_will_display = get_transient( $transient_name ) ) ) {
1622
			$has_children = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE parent = %d AND taxonomy = %s", $term->term_id, $term->taxonomy ) );
1623
1624
			if ( $has_children ) {
1625
				// Check terms have products inside - parents first. If products are found inside, subcats will be shown instead of products so we can return false.
1626
				if ( sizeof( get_objects_in_term( $has_children, $term->taxonomy ) ) > 0 ) {
1627
					$products_will_display = false;
1628
				} else {
1629
					// If we get here, the parents were empty so we're forced to check children
1630
					foreach ( $has_children as $term_id ) {
1631
						$children = get_term_children( $term_id, $term->taxonomy );
1632
1633
						if ( sizeof( get_objects_in_term( $children, $term->taxonomy ) ) > 0 ) {
1634
							$products_will_display = false;
1635
							break;
1636
						}
1637
					}
1638
				}
1639
			} else {
1640
				$products_will_display = true;
1641
			}
1642
		}
1643
1644
		set_transient( $transient_name, $products_will_display, DAY_IN_SECONDS * 30 );
1645
1646
		return $products_will_display;
1647
	}
1648
}
1649
1650
if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
1651
1652
	/**
1653
	 * Display product sub categories as thumbnails.
1654
	 *
1655
	 * @subpackage	Loop
1656
	 * @param array $args
1657
	 * @return null|boolean
1658
	 */
1659
	function woocommerce_product_subcategories( $args = array() ) {
1660
		global $wp_query;
1661
1662
		$defaults = array(
1663
			'before'        => '',
1664
			'after'         => '',
1665
			'force_display' => false,
1666
		);
1667
1668
		$args = wp_parse_args( $args, $defaults );
1669
1670
		extract( $args );
1671
1672
		// Main query only
1673
		if ( ! is_main_query() && ! $force_display ) {
1674
			return;
1675
		}
1676
1677
		// Don't show when filtering, searching or when on page > 1 and ensure we're on a product archive
1678
		if ( is_search() || is_filtered() || is_paged() || ( ! is_product_category() && ! is_shop() ) ) {
1679
			return;
1680
		}
1681
1682
		// Check categories are enabled
1683
		if ( is_shop() && '' === get_option( 'woocommerce_shop_page_display' ) ) {
1684
			return;
1685
		}
1686
1687
		// Find the category + category parent, if applicable
1688
		$term 			= get_queried_object();
1689
		$parent_id 		= empty( $term->term_id ) ? 0 : $term->term_id;
1690
1691
		if ( is_product_category() ) {
1692
			$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1693
1694
			switch ( $display_type ) {
1695
				case 'products' :
1696
					return;
1697
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1698
				case '' :
1699
					if ( '' === get_option( 'woocommerce_category_archive_display' ) ) {
1700
						return;
1701
					}
1702
				break;
1703
			}
1704
		}
1705
1706
		// NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work
1707
		$product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array(
1708
			'parent'       => $parent_id,
1709
			'menu_order'   => 'ASC',
1710
			'hide_empty'   => 0,
1711
			'hierarchical' => 1,
1712
			'taxonomy'     => 'product_cat',
1713
			'pad_counts'   => 1,
1714
		) ) );
1715
1716
		if ( ! apply_filters( 'woocommerce_product_subcategories_hide_empty', false ) ) {
1717
			$product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' );
1718
		}
1719
1720
		if ( $product_categories ) {
1721
			echo $before;
1722
1723
			foreach ( $product_categories as $category ) {
1724
				wc_get_template( 'content-product_cat.php', array(
1725
					'category' => $category,
1726
				) );
1727
			}
1728
1729
			// If we are hiding products disable the loop and pagination
1730
			if ( is_product_category() ) {
1731
				$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1732
1733
				switch ( $display_type ) {
1734
					case 'subcategories' :
1735
						$wp_query->post_count    = 0;
1736
						$wp_query->max_num_pages = 0;
1737
					break;
1738
					case '' :
1739
						if ( 'subcategories' === get_option( 'woocommerce_category_archive_display' ) ) {
1740
							$wp_query->post_count    = 0;
1741
							$wp_query->max_num_pages = 0;
1742
						}
1743
					break;
1744
				}
1745
			}
1746
1747
			if ( is_shop() && 'subcategories' === get_option( 'woocommerce_shop_page_display' ) ) {
1748
				$wp_query->post_count    = 0;
1749
				$wp_query->max_num_pages = 0;
1750
			}
1751
1752
			echo $after;
1753
1754
			return true;
1755
		}
1756
	}
1757
}
1758
1759
if ( ! function_exists( 'woocommerce_subcategory_thumbnail' ) ) {
1760
1761
	/**
1762
	 * Show subcategory thumbnails.
1763
	 *
1764
	 * @param mixed $category
1765
	 * @subpackage	Loop
1766
	 */
1767
	function woocommerce_subcategory_thumbnail( $category ) {
1768
		$small_thumbnail_size  	= apply_filters( 'subcategory_archive_thumbnail_size', 'shop_catalog' );
1769
		$dimensions    			= wc_get_image_size( $small_thumbnail_size );
1770
		$thumbnail_id  			= get_woocommerce_term_meta( $category->term_id, 'thumbnail_id', true );
1771
1772
		if ( $thumbnail_id ) {
1773
			$image        = wp_get_attachment_image_src( $thumbnail_id, $small_thumbnail_size );
1774
			$image        = $image[0];
1775
			$image_srcset = function_exists( 'wp_get_attachment_image_srcset' ) ? wp_get_attachment_image_srcset( $thumbnail_id, $small_thumbnail_size ) : false;
1776
			$image_sizes  = function_exists( 'wp_get_attachment_image_sizes' ) ? wp_get_attachment_image_sizes( $thumbnail_id, $small_thumbnail_size ) : false;
1777
		} else {
1778
			$image        = wc_placeholder_img_src();
1779
			$image_srcset = $image_sizes = false;
1780
		}
1781
1782
		if ( $image ) {
1783
			// Prevent esc_url from breaking spaces in urls for image embeds
1784
			// Ref: https://core.trac.wordpress.org/ticket/23605
1785
			$image = str_replace( ' ', '%20', $image );
1786
1787
			// Add responsive image markup if available
1788
			if ( $image_srcset && $image_sizes ) {
1789
				echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" srcset="' . esc_attr( $image_srcset ) . '" sizes="' . esc_attr( $image_sizes ) . '" />';
1790
			} else {
1791
				echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" />';
1792
			}
1793
		}
1794
	}
1795
}
1796
1797
if ( ! function_exists( 'woocommerce_order_details_table' ) ) {
1798
1799
	/**
1800
	 * Displays order details in a table.
1801
	 *
1802
	 * @param mixed $order_id
1803
	 * @subpackage	Orders
1804
	 */
1805
	function woocommerce_order_details_table( $order_id ) {
1806
		if ( ! $order_id ) return;
1807
1808
		wc_get_template( 'order/order-details.php', array(
1809
			'order_id' => $order_id,
1810
		) );
1811
	}
1812
}
1813
1814
1815
if ( ! function_exists( 'woocommerce_order_again_button' ) ) {
1816
1817
	/**
1818
	 * Display an 'order again' button on the view order page.
1819
	 *
1820
	 * @param object $order
1821
	 * @subpackage	Orders
1822
	 */
1823
	function woocommerce_order_again_button( $order ) {
1824
		if ( ! $order || ! $order->has_status( 'completed' ) || ! is_user_logged_in() ) {
1825
			return;
1826
		}
1827
1828
		wc_get_template( 'order/order-again.php', array(
1829
			'order' => $order,
1830
		) );
1831
	}
1832
}
1833
1834
/** Forms ****************************************************************/
1835
1836
if ( ! function_exists( 'woocommerce_form_field' ) ) {
1837
1838
	/**
1839
	 * Outputs a checkout/address form field.
1840
	 *
1841
	 * @subpackage	Forms
1842
	 * @param string $key
1843
	 * @param mixed $args
1844
	 * @param string $value (default: null)
1845
	 * @todo This function needs to be broken up in smaller pieces
1846
	 */
1847
	function woocommerce_form_field( $key, $args, $value = null ) {
1848
		$defaults = array(
1849
			'type'              => 'text',
1850
			'label'             => '',
1851
			'description'       => '',
1852
			'placeholder'       => '',
1853
			'maxlength'         => false,
1854
			'required'          => false,
1855
			'autocomplete'      => false,
1856
			'id'                => $key,
1857
			'class'             => array(),
1858
			'label_class'       => array(),
1859
			'input_class'       => array(),
1860
			'return'            => false,
1861
			'options'           => array(),
1862
			'custom_attributes' => array(),
1863
			'validate'          => array(),
1864
			'default'           => '',
1865
		);
1866
1867
		$args = wp_parse_args( $args, $defaults );
1868
		$args = apply_filters( 'woocommerce_form_field_args', $args, $key, $value );
1869
1870
		if ( $args['required'] ) {
1871
			$args['class'][] = 'validate-required';
1872
			$required = ' <abbr class="required" title="' . esc_attr__( 'required', 'woocommerce' ) . '">*</abbr>';
1873
		} else {
1874
			$required = '';
1875
		}
1876
1877
		$args['maxlength'] = ( $args['maxlength'] ) ? 'maxlength="' . absint( $args['maxlength'] ) . '"' : '';
1878
1879
		$args['autocomplete'] = ( $args['autocomplete'] ) ? 'autocomplete="' . esc_attr( $args['autocomplete'] ) . '"' : '';
1880
1881
		if ( is_string( $args['label_class'] ) ) {
1882
			$args['label_class'] = array( $args['label_class'] );
1883
		}
1884
1885
		if ( is_null( $value ) ) {
1886
			$value = $args['default'];
1887
		}
1888
1889
		// Custom attribute handling
1890
		$custom_attributes = array();
1891
1892 View Code Duplication
		if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
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...
1893
			foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
1894
				$custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
1895
			}
1896
		}
1897
1898
		if ( ! empty( $args['validate'] ) ) {
1899
			foreach ( $args['validate'] as $validate ) {
0 ignored issues
show
Bug introduced by
The expression $args['validate'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1900
				$args['class'][] = 'validate-' . $validate;
1901
			}
1902
		}
1903
1904
		$field = '';
1905
		$label_id = $args['id'];
1906
		$field_container = '<p class="form-row %1$s" id="%2$s">%3$s</p>';
1907
1908
		switch ( $args['type'] ) {
1909
			case 'country' :
1910
1911
				$countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries();
1912
1913
				if ( 1 === sizeof( $countries ) ) {
1914
1915
					$field .= '<strong>' . current( array_values( $countries ) ) . '</strong>';
1916
1917
					$field .= '<input type="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="' . current( array_keys( $countries ) ) . '" ' . implode( ' ', $custom_attributes ) . ' class="country_to_state" />';
1918
1919
				} else {
1920
1921
					$field = '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . $args['autocomplete'] . ' class="country_to_state country_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . '>' . '<option value="">' . __( 'Select a country&hellip;', 'woocommerce' ) . '</option>';
1922
1923 View Code Duplication
					foreach ( $countries as $ckey => $cvalue ) {
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...
1924
						$field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>';
1925
					}
1926
1927
					$field .= '</select>';
1928
1929
					$field .= '<noscript><input type="submit" name="woocommerce_checkout_update_totals" value="' . esc_attr__( 'Update country', 'woocommerce' ) . '" /></noscript>';
1930
1931
				}
1932
1933
				break;
1934
			case 'state' :
1935
1936
				/* Get Country */
1937
				$country_key = 'billing_state' === $key ? 'billing_country' : 'shipping_country';
1938
				$current_cc  = WC()->checkout->get_value( $country_key );
1939
				$states      = WC()->countries->get_states( $current_cc );
1940
1941
				if ( is_array( $states ) && empty( $states ) ) {
1942
1943
					$field_container = '<p class="form-row %1$s" id="%2$s" style="display: none">%3$s</p>';
1944
1945
					$field .= '<input type="hidden" class="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="" ' . implode( ' ', $custom_attributes ) . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" />';
1946
1947
				} elseif ( is_array( $states ) ) {
1948
1949
					$field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="state_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['autocomplete'] . '>
1950
						<option value="">' . __( 'Select a state&hellip;', 'woocommerce' ) . '</option>';
1951
1952 View Code Duplication
					foreach ( $states as $ckey => $cvalue ) {
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...
1953
						$field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>';
1954
					}
1955
1956
					$field .= '</select>';
1957
1958
				} else {
1959
1960
					$field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $value ) . '"  placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['autocomplete'] . ' name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
1961
1962
				}
1963
1964
				break;
1965
			case 'textarea' :
1966
1967
				$field .= '<textarea name="' . esc_attr( $key ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['maxlength'] . ' ' . $args['autocomplete'] . ' ' . ( empty( $args['custom_attributes']['rows'] ) ? ' rows="2"' : '' ) . ( empty( $args['custom_attributes']['cols'] ) ? ' cols="5"' : '' ) . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $value ) . '</textarea>';
1968
1969
				break;
1970 View Code Duplication
			case 'checkbox' :
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...
1971
1972
				$field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) . '" ' . implode( ' ', $custom_attributes ) . '>
1973
						<input type="' . esc_attr( $args['type'] ) . '" class="input-checkbox ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="1" ' . checked( $value, 1, false ) . ' /> '
1974
						 . $args['label'] . $required . '</label>';
1975
1976
				break;
1977
			case 'password' :
1978
			case 'text' :
1979
			case 'email' :
1980
			case 'tel' :
1981 View Code Duplication
			case 'number' :
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...
1982
1983
				$field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['maxlength'] . ' ' . $args['autocomplete'] . ' value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
1984
1985
				break;
1986
			case 'select' :
1987
1988
				$options = $field = '';
1989
1990
				if ( ! empty( $args['options'] ) ) {
1991
					foreach ( $args['options'] as $option_key => $option_text ) {
1992
						if ( '' === $option_key ) {
1993
							// If we have a blank option, select2 needs a placeholder
1994
							if ( empty( $args['placeholder'] ) ) {
1995
								$args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' );
1996
							}
1997
							$custom_attributes[] = 'data-allow_clear="true"';
1998
						}
1999
						$options .= '<option value="' . esc_attr( $option_key ) . '" ' . selected( $value, $option_key, false ) . '>' . esc_attr( $option_text ) . '</option>';
2000
					}
2001
2002
					$field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['autocomplete'] . '>
2003
							' . $options . '
2004
						</select>';
2005
				}
2006
2007
				break;
2008
			case 'radio' :
2009
2010
				$label_id = current( array_keys( $args['options'] ) );
2011
2012
				if ( ! empty( $args['options'] ) ) {
2013
					foreach ( $args['options'] as $option_key => $option_text ) {
0 ignored issues
show
Bug introduced by
The expression $args['options'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2014
						$field .= '<input type="radio" class="input-radio ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $option_key ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '"' . checked( $value, $option_key, false ) . ' />';
2015
						$field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) . '">' . $option_text . '</label>';
2016
					}
2017
				}
2018
2019
				break;
2020
		}
2021
2022
		if ( ! empty( $field ) ) {
2023
			$field_html = '';
2024
2025
			if ( $args['label'] && 'checkbox' != $args['type'] ) {
2026
				$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . $args['label'] . $required . '</label>';
2027
			}
2028
2029
			$field_html .= $field;
2030
2031
			if ( $args['description'] ) {
2032
				$field_html .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
2033
			}
2034
2035
			$container_class = 'form-row ' . esc_attr( implode( ' ', $args['class'] ) );
2036
			$container_id = esc_attr( $args['id'] ) . '_field';
2037
2038
			$after = ! empty( $args['clear'] ) ? '<div class="clear"></div>' : '';
2039
2040
			$field = sprintf( $field_container, $container_class, $container_id, $field_html ) . $after;
2041
		}
2042
2043
		$field = apply_filters( 'woocommerce_form_field_' . $args['type'], $field, $key, $args, $value );
2044
2045
		if ( $args['return'] ) {
2046
			return $field;
2047
		} else {
2048
			echo $field;
2049
		}
2050
	}
2051
}
2052
2053
if ( ! function_exists( 'get_product_search_form' ) ) {
2054
2055
	/**
2056
	 * Display product search form.
2057
	 *
2058
	 * Will first attempt to locate the product-searchform.php file in either the child or.
2059
	 * the parent, then load it. If it doesn't exist, then the default search form.
2060
	 * will be displayed.
2061
	 *
2062
	 * The default searchform uses html5.
2063
	 *
2064
	 * @subpackage	Forms
2065
	 * @param bool $echo (default: true)
2066
	 * @return string
2067
	 */
2068
	function get_product_search_form( $echo = true  ) {
2069
		global $product_search_form_index;
2070
2071
		ob_start();
2072
2073
		if ( empty( $product_search_form_index ) ) {
2074
			$product_search_form_index = 0;
2075
		}
2076
2077
		do_action( 'pre_get_product_search_form' );
2078
2079
		wc_get_template( 'product-searchform.php', array(
2080
			'index' => $product_search_form_index++,
2081
		) );
2082
2083
		$form = apply_filters( 'get_product_search_form', ob_get_clean() );
2084
2085
		if ( $echo ) {
2086
			echo $form;
2087
		} else {
2088
			return $form;
2089
		}
2090
	}
2091
}
2092
2093
if ( ! function_exists( 'woocommerce_output_auth_header' ) ) {
2094
2095
	/**
2096
	 * Output the Auth header.
2097
	 */
2098
	function woocommerce_output_auth_header() {
2099
		wc_get_template( 'auth/header.php' );
2100
	}
2101
}
2102
2103
if ( ! function_exists( 'woocommerce_output_auth_footer' ) ) {
2104
2105
	/**
2106
	 * Output the Auth footer.
2107
	 */
2108
	function woocommerce_output_auth_footer() {
2109
		wc_get_template( 'auth/footer.php' );
2110
	}
2111
}
2112
2113
if ( ! function_exists( 'woocommerce_single_variation' ) ) {
2114
2115
	/**
2116
	 * Output placeholders for the single variation.
2117
	 */
2118
	function woocommerce_single_variation() {
2119
		echo '<div class="woocommerce-variation single_variation"></div>';
2120
	}
2121
}
2122
2123
if ( ! function_exists( 'woocommerce_single_variation_add_to_cart_button' ) ) {
2124
2125
	/**
2126
	 * Output the add to cart button for variations.
2127
	 */
2128
	function woocommerce_single_variation_add_to_cart_button() {
2129
		wc_get_template( 'single-product/add-to-cart/variation-add-to-cart-button.php' );
2130
	}
2131
}
2132
2133
if ( ! function_exists( 'wc_dropdown_variation_attribute_options' ) ) {
2134
2135
	/**
2136
	 * Output a list of variation attributes for use in the cart forms.
2137
	 *
2138
	 * @param array $args
2139
	 * @since 2.4.0
2140
	 */
2141
	function wc_dropdown_variation_attribute_options( $args = array() ) {
2142
		$args = wp_parse_args( apply_filters( 'woocommerce_dropdown_variation_attribute_options_args', $args ), array(
2143
			'options'          => false,
2144
			'attribute'        => false,
2145
			'product'          => false,
2146
			'selected' 	       => false,
2147
			'name'             => '',
2148
			'id'               => '',
2149
			'class'            => '',
2150
			'show_option_none' => __( 'Choose an option', 'woocommerce' ),
2151
		) );
2152
2153
		$options   = $args['options'];
2154
		$product   = $args['product'];
2155
		$attribute = $args['attribute'];
2156
		$name      = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute );
2157
		$id        = $args['id'] ? $args['id'] : sanitize_title( $attribute );
2158
		$class     = $args['class'];
2159
2160
		if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
2161
			$attributes = $product->get_variation_attributes();
2162
			$options    = $attributes[ $attribute ];
2163
		}
2164
2165
		$html = '<select id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '" name="' . esc_attr( $name ) . '" data-attribute_name="attribute_' . esc_attr( sanitize_title( $attribute ) ) . '">';
2166
2167
		if ( $args['show_option_none'] ) {
2168
			$html .= '<option value="">' . esc_html( $args['show_option_none'] ) . '</option>';
2169
		}
2170
2171
		if ( ! empty( $options ) ) {
2172
			if ( $product && taxonomy_exists( $attribute ) ) {
2173
				// Get terms if this is a taxonomy - ordered. We need the names too.
2174
				$terms = wc_get_product_terms( $product->id, $attribute, array( 'fields' => 'all' ) );
2175
2176 View Code Duplication
				foreach ( $terms as $term ) {
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...
2177
					if ( in_array( $term->slug, $options ) ) {
2178
						$html .= '<option value="' . esc_attr( $term->slug ) . '" ' . selected( sanitize_title( $args['selected'] ), $term->slug, false ) . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name ) ) . '</option>';
2179
					}
2180
				}
2181
			} else {
2182
				foreach ( $options as $option ) {
2183
					// This handles < 2.4.0 bw compatibility where text attributes were not sanitized.
2184
					$selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false );
2185
					$html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ) . '</option>';
2186
				}
2187
			}
2188
		}
2189
2190
		$html .= '</select>';
2191
2192
		echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args );
2193
	}
2194
}
2195
2196
if ( ! function_exists( 'woocommerce_account_content' ) ) {
2197
2198
	/**
2199
	 * My Account content output.
2200
	 */
2201
	function woocommerce_account_content() {
2202
		global $wp;
2203
2204
		foreach ( $wp->query_vars as $key => $value ) {
2205
			// Ignore pagename param.
2206
			if ( 'pagename' === $key ) {
2207
				continue;
2208
			}
2209
2210
			if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
2211
				do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
2212
				return;
2213
			}
2214
		}
2215
2216
		// No endpoint found? Default to dashboard.
2217
		wc_get_template( 'myaccount/dashboard.php', array(
2218
			'current_user' => get_user_by( 'id', get_current_user_id() ),
2219
		) );
2220
	}
2221
}
2222
2223
if ( ! function_exists( 'woocommerce_account_navigation' ) ) {
2224
2225
	/**
2226
	 * My Account navigation template.
2227
	 */
2228
	function woocommerce_account_navigation() {
2229
		wc_get_template( 'myaccount/navigation.php' );
2230
	}
2231
}
2232
2233
if ( ! function_exists( 'woocommerce_account_orders' ) ) {
2234
2235
	/**
2236
	 * My Account > Orders template.
2237
	 *
2238
	 * @param int $current_page Current page number.
2239
	 */
2240
	function woocommerce_account_orders( $current_page ) {
2241
		$current_page    = empty( $current_page ) ? 1 : absint( $current_page );
2242
		$customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array( 'customer' => get_current_user_id(), 'page' => $current_page, 'paginate' => true ) ) );
2243
2244
		wc_get_template(
2245
			'myaccount/orders.php',
2246
			array(
2247
				'current_page' => absint( $current_page ),
2248
				'customer_orders' => $customer_orders,
2249
				'has_orders' => 0 < $customer_orders->total,
2250
			)
2251
		);
2252
	}
2253
}
2254
2255
if ( ! function_exists( 'woocommerce_account_view_order' ) ) {
2256
2257
	/**
2258
	 * My Account > View order template.
2259
	 *
2260
	 * @param int $order_id Order ID.
2261
	 */
2262
	function woocommerce_account_view_order( $order_id ) {
2263
		WC_Shortcode_My_Account::view_order( absint( $order_id ) );
2264
	}
2265
}
2266
2267
if ( ! function_exists( 'woocommerce_account_downloads' ) ) {
2268
2269
	/**
2270
	 * My Account > Downloads template.
2271
	 */
2272
	function woocommerce_account_downloads() {
2273
		wc_get_template( 'myaccount/downloads.php' );
2274
	}
2275
}
2276
2277
if ( ! function_exists( 'woocommerce_account_edit_address' ) ) {
2278
2279
	/**
2280
	 * My Account > Edit address template.
2281
	 *
2282
	 * @param string $type Address type.
2283
	 */
2284
	function woocommerce_account_edit_address( $type ) {
2285
		$type = wc_edit_address_i18n( sanitize_title( $type ), true );
2286
2287
		WC_Shortcode_My_Account::edit_address( $type );
2288
	}
2289
}
2290
2291
if ( ! function_exists( 'woocommerce_account_payment_methods' ) ) {
2292
2293
	/**
2294
	 * My Account > Downloads template.
2295
	 */
2296
	function woocommerce_account_payment_methods() {
2297
		wc_get_template( 'myaccount/payment-methods.php' );
2298
	}
2299
}
2300
2301
if ( ! function_exists( 'woocommerce_account_add_payment_method' ) ) {
2302
2303
	/**
2304
	 * My Account > Add payment method template.
2305
	 */
2306
	function woocommerce_account_add_payment_method() {
2307
		WC_Shortcode_My_Account::add_payment_method();
2308
	}
2309
}
2310
2311
if ( ! function_exists( 'woocommerce_account_edit_account' ) ) {
2312
2313
	/**
2314
	 * My Account > Edit account template.
2315
	 */
2316
	function woocommerce_account_edit_account() {
2317
		WC_Shortcode_My_Account::edit_account();
2318
	}
2319
}
2320
2321
if ( ! function_exists( 'wc_no_products_found' ) ) {
2322
2323
	/**
2324
	 * Show no products found message.
2325
	 */
2326
	function wc_no_products_found() {
2327
		wc_get_template( 'loop/no-products-found.php' );
2328
	}
2329
}
2330
2331
2332
if ( ! function_exists( 'wc_get_email_order_items' ) ) {
2333
	/**
2334
	 * Get HTML for the order items to be shown in emails.
2335
	 * @param WC_Order $order
2336
	 * @param array $args
2337
	 * @since 2.7.0
2338
	 */
2339
	function wc_get_email_order_items( $order, $args = array() ) {
2340
		ob_start();
2341
2342
		$defaults = array(
2343
			'show_sku'      => false,
2344
			'show_image'    => false,
2345
			'image_size'    => array( 32, 32 ),
2346
			'plain_text'    => false,
2347
			'sent_to_admin' => false,
2348
		);
2349
2350
		$args     = wp_parse_args( $args, $defaults );
2351
		$template = $args['plain_text'] ? 'emails/plain/email-order-items.php' : 'emails/email-order-items.php';
2352
2353
		wc_get_template( $template, array(
2354
			'order'               => $order,
2355
			'items'               => $order->get_items(),
2356
			'show_download_links' => $order->is_download_permitted(),
2357
			'show_sku'            => $args['show_sku'],
2358
			'show_purchase_note'  => $order->is_paid(),
2359
			'show_image'          => $args['show_image'],
2360
			'image_size'          => $args['image_size'],
2361
			'plain_text'          => $args['plain_text'],
2362
			'sent_to_admin'       => $args['sent_to_admin'],
2363
		) );
2364
2365
		return apply_filters( 'woocommerce_email_order_items_table', ob_get_clean(), $order );
2366
	}
2367
}
2368
2369
if ( ! function_exists( 'wc_display_item_meta' ) ) {
2370
	/**
2371
	 * Display item meta data.
2372
	 * @since  2.7.0
2373
	 * @param  WC_Item $item
2374
	 * @param  array   $args
2375
	 * @return string|void
2376
	 */
2377
	function wc_display_item_meta( $item, $args = array() ) {
2378
		$strings = array();
2379
		$html    = '';
2380
		$args    = wp_parse_args( $args, array(
2381
			'before'    => '<ul class="wc-item-meta"><li>',
2382
			'after'     => '</li></ul>',
2383
			'separator' => '</li><li>',
2384
			'echo'      => true,
2385
			'autop'     => false,
2386
		) );
2387
2388
		foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Item as the method get_formatted_meta_data() does only exist in the following sub-classes of WC_Item: WC_Order_Item_Product. 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...
2389
			if ( '_' === substr( $meta->key, 0, 1 ) ) {
2390
				continue;
2391
			}
2392
			$value = $args['autop'] ? wp_kses_post( wpautop( make_clickable( $meta->display_value ) ) ) : wp_kses_post( make_clickable( $meta->display_value ) );
2393
			$strings[] = '<strong class="wc-item-meta-label">' . wp_kses_post( $meta->display_key ) . ':</strong> ' . $value;
2394
		}
2395
2396 View Code Duplication
		if ( $strings ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $strings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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...
2397
			$html = $args['before'] . implode( $args['separator'], $strings ) . $args['after'];
2398
		}
2399
2400
		$html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args );
2401
2402
		if ( $args['echo'] ) {
2403
			echo $html;
2404
		} else {
2405
			return $html;
2406
		}
2407
	}
2408
}
2409
2410
if ( ! function_exists( 'wc_display_item_downloads' ) ) {
2411
	/**
2412
	 * Display item download links.
2413
	 * @since  2.7.0
2414
	 * @param  WC_Item $item
2415
	 * @param  array   $args
2416
	 * @return string|void
2417
	 */
2418
	function wc_display_item_downloads( $item, $args = array() ) {
2419
		$strings = array();
2420
		$html    = '';
2421
		$args    = wp_parse_args( $args, array(
2422
			'before'    => '<ul class ="wc-item-downloads"><li>',
2423
			'after'     => '</li></ul>',
2424
			'separator' => '</li><li>',
2425
			'echo'      => true,
2426
			'show_url'  => false,
2427
		) );
2428
2429
		if ( is_object( $item ) && $item->is_type( 'line_item' ) && ( $downloads = $item->get_item_downloads() ) ) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Item as the method get_item_downloads() does only exist in the following sub-classes of WC_Item: WC_Order_Item_Product. 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...
2430
			$i = 0;
2431
			foreach ( $downloads as $file ) {
2432
				$i ++;
2433
2434
				if ( $args['show_url'] ) {
2435
					$strings[] = '<strong class="wc-item-download-label">' . esc_html( $file['name'] ) . ':</strong> ' . esc_html( $file['download_url'] );
2436
				} else {
2437
					$prefix = sizeof( $downloads ) > 1 ? sprintf( __( 'Download %d', 'woocommerce' ), $i ) : __( 'Download', 'woocommerce' );
2438
					$strings[] = '<strong class="wc-item-download-label">' . $prefix . ':</strong> <a href="' . esc_url( $file['download_url'] ) . '" target="_blank">' . esc_html( $file['name'] ) . '</a>';
2439
				}
2440
			}
2441
		}
2442
2443 View Code Duplication
		if ( $strings ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $strings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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...
2444
			$html = $args['before'] . implode( $args['separator'], $strings ) . $args['after'];
2445
		}
2446
2447
		$html = apply_filters( 'woocommerce_display_item_downloads', $html, $item, $args );
2448
2449
		if ( $args['echo'] ) {
2450
			echo $html;
2451
		} else {
2452
			return $html;
2453
		}
2454
	}
2455
}
2456
2457
if ( ! function_exists( 'wc_display_item_data' ) ) {
2458
	/**
2459
	 * Gets and formats a list of cart item data + variations for display on the frontend. @todo
2460
	 *
2461
	 * @since 2.7.0
2462
	 * @param array $cart_item
2463
	 * @param bool $flat (default: false)
2464
	 * @return string
2465
	 */
2466
	function wc_display_item_data( $cart_item, $flat = false ) {
2467
		ob_start();
2468
		$item_data = array();
2469
2470
		if ( ! empty( $cart_item['data']->variation_id ) && is_array( $cart_item['variation'] ) ) {
2471
			foreach ( $cart_item['variation'] as $name => $value ) {
2472
				if ( '' === $value ) {
2473
					continue;
2474
				}
2475
2476
				$taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', $name ) );
2477
2478
				// If this is a term slug, get the term's nice name
2479
				if ( taxonomy_exists( $taxonomy ) && ( $term = get_term_by( 'slug', $value, $taxonomy ) ) && ! is_wp_error( $term ) ) {
2480
					$value = $term->name;
2481
					$label = wc_attribute_label( $taxonomy );
2482
2483
				// If this is a custom option slug, get the options name
2484
				} else {
2485
					$value              = apply_filters( 'woocommerce_variation_option_name', $value );
2486
					$product_attributes = $cart_item['data']->get_attributes();
2487
					if ( isset( $product_attributes[ str_replace( 'attribute_', '', $name ) ] ) ) {
2488
						$label = wc_attribute_label( $product_attributes[ str_replace( 'attribute_', '', $name ) ]['name'] );
2489
					} else {
2490
						$label = $name;
2491
					}
2492
				}
2493
				$item_data[] = array(
2494
					'key'   => $label,
2495
					'value' => $value,
2496
				);
2497
			}
2498
		}
2499
2500
		// Filter item data to allow 3rd parties to add more to the array
2501
		$item_data = apply_filters( 'woocommerce_get_item_data', $item_data, $cart_item );
2502
2503
		// Format item data ready to display
2504
		foreach ( $item_data as $key => $data ) {
2505
			// Set hidden to true to not display meta on cart.
2506
			if ( ! empty( $data['hidden'] ) ) {
2507
				unset( $item_data[ $key ] );
2508
				continue;
2509
			}
2510
			$item_data[ $key ]['key']     = ! empty( $data['key'] ) ? $data['key'] : $data['name'];
2511
			$item_data[ $key ]['display'] = ! empty( $data['display'] ) ? $data['display'] : $data['value'];
2512
		}
2513
2514
		// Output flat or in list format
2515
		if ( sizeof( $item_data ) > 0 ) {
2516
			ob_start();
2517
2518
			if ( $flat ) {
2519
				foreach ( $item_data as $data ) {
2520
					echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['display'] ) . "\n";
2521
				}
2522
			} else {
2523
				wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) );
2524
			}
2525
2526
			return ob_get_clean();
2527
		}
2528
2529
		return ob_get_clean();
2530
	}
2531
}
2532