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

wc-cart-functions.php ➔ wc_load_persistent_cart()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 3
nop 0
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * WooCommerce Cart Functions
4
 *
5
 * Functions for cart specific things.
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
 * Prevent password protected products being added to the cart.
19
 *
20
 * @param  bool $passed
21
 * @param  int $product_id
22
 * @return bool
23
 */
24
function wc_protected_product_add_to_cart( $passed, $product_id ) {
25
	if ( post_password_required( $product_id ) ) {
26
		$passed = false;
27
		wc_add_notice( __( 'This product is protected and cannot be purchased.', 'woocommerce' ), 'error' );
28
	}
29
	return $passed;
30
}
31
add_filter( 'woocommerce_add_to_cart_validation', 'wc_protected_product_add_to_cart', 10, 2 );
32
33
/**
34
 * Add a product to the cart.
35
 *
36
 * Validates the product can be added, then adds it if valid.
37
 *
38
 * @since 2.7.0
39
 * @param int $product_or_variation_id contains the id of the product to add to the cart.
40
 * @param int $quantity contains the quantity of the item to add
41
 * @param array $data extra cart item data we want to pass into the item, including any data about the variation if applicable.
42
 * @return string|bool $cart_item_key that was added or false if validation failed.
43
 */
44
function wc_add_to_cart( $product_or_variation_id = 0, $quantity = 1, $data = array() ) {
45
	try {
46
		$product = wc_get_product( $product_or_variation_id );
47
48
		if ( empty( $product ) || ! $product->is_purchasable() ) {
49
			throw new Exception( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ) );
50
		}
51
52
		$quantity = max( 0, apply_filters( 'woocommerce_add_to_cart_quantity', $quantity, $product, $data ) );
53
		$data     = (array) apply_filters( 'woocommerce_add_to_cart_data', $data, $product );
54
55
		wc_add_to_cart_validate_stock( $product, $quantity );
56
57
		$cart_item_key = WC()->cart->add_item( array(
58
			'product'  => $product,
59
			'quantity' => $quantity,
60
			'data'     => $data,
61
		) );
62
63
		do_action(
64
			'woocommerce_add_to_cart',
65
			$cart_item_key,
66
			$product->get_id(),
67
			$quantity,
68
			is_callable( array( $product, 'get_variation_id' ) ) ? $product->get_variation_id() : 0,
69
			! empty( $data['variation'] ) ? $data['variation'] : array(),
70
			$data
71
		);
72
73
		return $cart_item_key;
74
	} catch ( Exception $e ) {
75
		wc_add_notice( $e->getMessage(), 'error' );
76
		return false;
77
	}
78
}
79
80
/**
81
 * Check product has enough stock to be added to cart.
82
 * @since 2.7.0
83
 * @param WC_Product $product
84
 * @param int $adding_quantity
85
 * @throws Exception
86
 */
87
function wc_add_to_cart_validate_stock( $product, $adding_quantity = 0 ) {
88
	$products_qty_in_cart  = wc_cart_item_quantities();
89
	$managing_stock        = $product->managing_stock();
90
	$check_variation_stock = $product->is_type( 'variation' ) && true === $managing_stock;
91
	$check_id              = $check_variation_stock ? $product->variation_id : $product->id;
92
	$in_cart_qty           = isset( $products_qty_in_cart[ $check_id ] ) ? $products_qty_in_cart[ $check_id ] : 0;
93
94
	if ( ! $product->is_in_stock() ) {
95
		throw new Exception( sprintf( __( 'You cannot add &quot;%s&quot; to the cart because the product is out of stock.', 'woocommerce' ), $product->get_title() ) );
96
	}
97
98
	if ( $product->is_sold_individually() && $in_cart_qty && $adding_quantity ) {
99
		throw new Exception( sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __( 'View Cart', 'woocommerce' ), sprintf( __( 'You cannot add another &quot;%s&quot; to your cart.', 'woocommerce' ), $product->get_title() ) ) );
100
	}
101
102
	if ( ! $product->has_enough_stock( $adding_quantity ) ) {
103
		throw new Exception( sprintf(__( 'You cannot add that amount of &quot;%s&quot; to the cart because there is not enough stock (%s remaining).', 'woocommerce' ), $product->get_title(), $product->get_stock_quantity() ) );
104
	}
105
106
	/**
107
	 * Check stock based on all items in the cart.
108
	 */
109
	if ( ! $product->has_enough_stock( $in_cart_qty + $adding_quantity ) ) {
110
		throw new Exception( '<a href="' . esc_url( wc_get_cart_url() ) . '" class="button wc-forward">' . __( 'View Cart', 'woocommerce' ) . '</a> ' . sprintf( __( 'You cannot add that amount to the cart &mdash; we have %s in stock and you already have %s in your cart.', 'woocommerce' ), $product->get_stock_quantity(), $check_qty ) );
111
	}
112
113
	/**
114
	 * Finally consider any held stock, from pending orders.
115
	 */
116
	if ( ! $product->has_enough_stock( $in_cart_qty + $adding_quantity + wc_get_held_stock_count( $product ) ) ) {
117
		throw new Exception( '<a href="' . esc_url( wc_get_cart_url() ) . '" class="button wc-forward">' . __( 'View Cart', 'woocommerce' ) . '</a> ' . sprintf(__( 'Sorry, we do not have enough "%s" in stock to fulfill your order right now. Please try again in %d minutes or edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_title(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
118
	}
119
}
120
121
/**
122
 * Get cart items quantities - merged so we can do accurate stock checks on items across multiple lines.
123
 * @since 2.7.0
124
 * @return array
125
 */
126
function wc_cart_item_quantities() {
127
	$quantities = array();
128
129
	foreach ( WC()->cart->get_cart() as $cart_item_key => $item ) {
130
		$id                = $item->get_product_id();
131
		$quantities[ $id ] = isset( $quantities[ $id ] ) ? $quantities[ $id ] + $item->get_quantity() : $item->get_quantity();
132
	}
133
134
	return $quantities;
135
}
136
137
/**
138
 * Clears the cart session when called.
139
 */
140
function wc_empty_cart() {
141
	if ( ! isset( WC()->cart ) || '' === WC()->cart ) {
142
		WC()->cart = new WC_Cart();
143
	}
144
	WC()->cart->empty_cart();
145
}
146
147
/**
148
 * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
149
 *
150
 * Do not use for redirects, use {@see wp_get_referer()} instead.
151
 *
152
 * @since 2.6.1
153
 * @return string|false Referer URL on success, false on failure.
154
 */
155
function wc_get_raw_referer() {
156
	if ( function_exists( 'wp_get_raw_referer' ) ) {
157
		return wp_get_raw_referer();
158
	}
159
160
	if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
161
		return wp_unslash( $_REQUEST['_wp_http_referer'] );
162
	} elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
163
		return wp_unslash( $_SERVER['HTTP_REFERER'] );
164
	}
165
166
	return false;
167
}
168
169
/**
170
 * Add to cart messages.
171
 *
172
 * @access public
173
 * @param int|array $products
174
 * @param bool $show_qty Should qty's be shown? Added in 2.6.0
175
 * @param bool $return Return message rather than add it.
176
 */
177
function wc_add_to_cart_message( $products, $show_qty = false, $return = false ) {
178
	$titles = array();
179
	$count  = 0;
180
181
	if ( ! is_array( $products ) ) {
182
		$products = array( $products => 1 );
183
		$show_qty = false;
184
	}
185
186
	if ( ! $show_qty ) {
187
		$products = array_fill_keys( array_keys( $products ), 1 );
188
	}
189
190
	foreach ( $products as $product_id => $qty ) {
191
		$titles[] = ( $qty > 1 ? absint( $qty ) . ' &times; ' : '' ) . sprintf( _x( '&ldquo;%s&rdquo;', 'Item name in quotes', 'woocommerce' ), strip_tags( get_the_title( $product_id ) ) );
192
		$count += $qty;
193
	}
194
195
	$titles     = array_filter( $titles );
196
	$added_text = sprintf( _n( '%s has been added to your cart.', '%s have been added to your cart.', $count, 'woocommerce' ), wc_format_list_of_items( $titles ) );
197
198
	// Output success messages
199
	if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
200
		$return_to = apply_filters( 'woocommerce_continue_shopping_redirect', wc_get_raw_referer() ? wp_validate_redirect( wc_get_raw_referer(), false ) : wc_get_page_permalink( 'shop' ) );
201
		$message   = sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', esc_url( $return_to ), esc_html__( 'Continue Shopping', 'woocommerce' ), esc_html( $added_text ) );
202
	} else {
203
		$message   = sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', esc_url( wc_get_page_permalink( 'cart' ) ), esc_html__( 'View Cart', 'woocommerce' ), esc_html( $added_text ) );
204
	}
205
206
	$message = apply_filters( 'wc_add_to_cart_message', $message, $product_id );
207
208
	if ( $return ) {
209
		return $message;
210
	} else {
211
		wc_add_notice( $message );
212
	}
213
}
214
215
/**
216
 * Comma separate a list of item names, and replace final comma with 'and'
217
 * @param  array $items
218
 * @return string
219
 */
220
function wc_format_list_of_items( $items ) {
221
	$item_string = '';
222
223
	foreach ( $items as $key => $item ) {
224
		$item_string .= $item;
225
226
		if ( sizeof( $items ) === $key + 2 ) {
227
			$item_string .= ' ' . __( 'and', 'woocommerce' ) . ' ';
228
		} elseif ( sizeof( $items ) !== $key + 1 ) {
229
			$item_string .= ', ';
230
		}
231
	}
232
233
	return $item_string;
234
}
235
236
/**
237
 * Clear cart after payment.
238
 *
239
 * @access public
240
 */
241
function wc_clear_cart_after_payment() {
242
	global $wp;
243
244
	if ( ! empty( $wp->query_vars['order-received'] ) ) {
245
246
		$order_id  = absint( $wp->query_vars['order-received'] );
247
		$order_key = isset( $_GET['key'] ) ? wc_clean( $_GET['key'] ) : '';
248
249 View Code Duplication
		if ( $order_id > 0 ) {
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...
250
			$order = wc_get_order( $order_id );
251
252
			if ( $order->get_order_key() === $order_key ) {
253
				WC()->cart->empty_cart();
254
			}
255
		}
256
	}
257
258
	if ( WC()->session->order_awaiting_payment > 0 ) {
259
		$order = wc_get_order( WC()->session->order_awaiting_payment );
260
261
		if ( $order && $order->get_id() > 0 ) {
262
			// If the order has not failed, or is not pending, the order must have gone through
263
			if ( ! $order->has_status( array( 'failed', 'pending', 'cancelled' ) ) ) {
264
				WC()->cart->empty_cart();
265
			}
266
		}
267
	}
268
}
269
add_action( 'get_header', 'wc_clear_cart_after_payment' );
270
271
/**
272
 * Get shipping methods.
273
 *
274
 * @access public
275
 */
276
function wc_cart_totals_shipping_html() {
277
	$packages = WC()->shipping->get_packages();
278
279
	foreach ( $packages as $key => $package ) {
280
		$chosen_method = wc_get_chosen_shipping_method_for_package( $key, $package );
281
		$product_names = array();
282
		// @codingStandardsIgnoreStart
283
		$package_name  = apply_filters( 'woocommerce_shipping_package_name', sprintf( _n( 'Shipping', 'Shipping %d', ( $key + 1 ), 'woocommerce' ), ( $key + 1 ) ), $key, $package );
284
		// @codingStandardsIgnoreEnd
285
		//
286
		if ( sizeof( $packages ) > 1 ) {
287
			foreach ( $package['contents'] as $item_id => $values ) {
288
				$product_names[] = $values['data']->get_title() . ' &times;' . $values['quantity'];
289
			}
290
		}
291
292
		if ( 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) && WC()->shipping->get_shipping_zone() ) {
293
			$package_name .= ' - ' . WC()->shipping->get_shipping_zone()->get_zone_name();
294
		}
295
296
		wc_get_template( 'cart/cart-shipping.php', array(
297
			'package'              => $package,
298
			'available_methods'    => $package['rates'],
299
			'show_package_details' => sizeof( $packages ) > 1,
300
			'package_details'      => implode( ', ', $product_names ),
301
			'package_name'         => $package_name,
302
			'index'                => $key,
303
			'chosen_method'        => $chosen_method,
304
		) );
305
	}
306
}
307
308
/**
309
 * Get taxes total. Used when not showing itemized taxes.
310
 */
311
function wc_cart_totals_taxes_total_html() {
312
	echo apply_filters( 'woocommerce_cart_totals_taxes_total_html', wc_price( WC()->cart->get_taxes_total() ) );
313
}
314
315
/**
316
 * Get a coupon label.
317
 *
318
 * @access public
319
 * @param string $coupon
320
 * @param bool $echo or return
321
 */
322
function wc_cart_totals_coupon_label( $coupon, $echo = true ) {
323
	if ( is_string( $coupon ) ) {
324
		$coupon = new WC_Coupon( $coupon );
325
	}
326
327
	$label = apply_filters( 'woocommerce_cart_totals_coupon_label', esc_html( __( 'Coupon:', 'woocommerce' ) . ' ' . $coupon->get_code() ), $coupon );
328
329
	if ( $echo ) {
330
		echo $label;
331
	} else {
332
		return $label;
333
	}
334
}
335
336
/**
337
 * Get a coupon value.
338
 *
339
 * @access public
340
 * @param string $coupon
341
 */
342
function wc_cart_totals_coupon_html( $coupon ) {
343
	if ( is_string( $coupon ) ) {
344
		$coupon = new WC_Coupon( $coupon );
345
	}
346
347
	$value  = array();
348
349
	if ( $amount = WC()->cart->get_coupon_discount_amount( $coupon->get_code(), WC()->cart->display_cart_ex_tax ) ) {
350
		$discount_html = '-' . wc_price( $amount );
351
	} else {
352
		$discount_html = '';
353
	}
354
355
	$value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon );
356
357
	if ( $coupon->get_free_shipping() ) {
358
		$value[] = __( 'Free shipping coupon', 'woocommerce' );
359
	}
360
361
	// get rid of empty array elements
362
	$value = array_filter( $value );
363
	$value = implode( ', ', $value ) . ' <a href="' . esc_url( add_query_arg( 'remove_coupon', urlencode( $coupon->get_code() ), defined( 'WOOCOMMERCE_CHECKOUT' ) ? wc_get_checkout_url() : wc_get_cart_url() ) ) . '" class="woocommerce-remove-coupon" data-coupon="' . esc_attr( $coupon->get_code() ) . '">' . __( '[Remove]', 'woocommerce' ) . '</a>';
364
365
	echo apply_filters( 'woocommerce_cart_totals_coupon_html', $value, $coupon );
366
}
367
368
/**
369
 * Get order total html including inc tax if needed.
370
 */
371
function wc_cart_totals_order_total_html() {
372
	$value = '<strong>' . wc_price( WC()->cart->get_total() ) . '</strong> ';
373
374
	// If prices are tax inclusive, show taxes here
375
	if ( wc_tax_enabled() && wc_cart_prices_include_tax() ) {
376
		$tax_string_array = array();
377
378
		if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
379
			foreach ( WC()->cart->get_tax_totals() as $code => $tax )
380
				$tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label );
381
		} else {
382
			$tax_string_array[] = sprintf( '%s %s', wc_price( WC()->cart->get_taxes_total( true, true ) ), WC()->countries->tax_or_vat() );
383
		}
384
385
		if ( ! empty( $tax_string_array ) ) {
386
			$taxable_address = WC()->customer->get_taxable_address();
387
			$estimated_text  = WC()->customer->is_customer_outside_base() && ! WC()->customer->has_calculated_shipping()
388
				? sprintf( ' ' . __( 'estimated for %s', 'woocommerce' ), WC()->countries->estimated_for_prefix( $taxable_address[0] ) . WC()->countries->countries[ $taxable_address[0] ] )
389
				: '';
390
			$value .= '<small class="includes_tax">' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) . $estimated_text ) . '</small>';
391
		}
392
	}
393
394
	echo apply_filters( 'woocommerce_cart_totals_order_total_html', $value );
395
}
396
397
/**
398
 * Get the fee value.
399
 *
400
 * @param object $fee
401
 */
402
function wc_cart_totals_fee_html( $fee ) {
403
	$cart_totals_fee_html = ! wc_cart_prices_include_tax() ? wc_price( $fee->amount ) : wc_price( $fee->amount + $fee->tax );
404
405
	echo apply_filters( 'woocommerce_cart_totals_fee_html', $cart_totals_fee_html, $fee );
406
}
407
408
/**
409
 * Get a shipping methods full label including price.
410
 * @param  WC_Shipping_Rate $method
411
 * @return string
412
 */
413
function wc_cart_totals_shipping_method_label( $method ) {
414
	$label = $method->get_label();
415
416
	if ( $method->cost > 0 ) {
417
		if ( ! wc_cart_prices_include_tax() ) {
418
			$label .= ': ' . wc_price( $method->cost );
419
			if ( $method->get_shipping_tax() > 0 && wc_prices_include_tax() ) {
420
				$label .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
421
			}
422
		} else {
423
			$label .= ': ' . wc_price( $method->cost + $method->get_shipping_tax() );
424 View Code Duplication
			if ( $method->get_shipping_tax() > 0 && ! wc_prices_include_tax() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
425
				$label .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
426
			}
427
		}
428
	}
429
430
	return apply_filters( 'woocommerce_cart_shipping_method_full_label', $label, $method );
431
}
432
433
/**
434
 * Round discount.
435
 *
436
 * @param  float $value
437
 * @param  int $precision
438
 * @return float
439
 */
440
function wc_cart_round_discount( $value, $precision ) {
441
	if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
442
		return round( $value, $precision, WC_DISCOUNT_ROUNDING_MODE );
443
	} else {
444
		return round( $value, $precision );
445
	}
446
}
447
448
/**
449
 * Gets chosen shipping method IDs from chosen_shipping_methods session, without instance IDs.
450
 * @since  2.6.2
451
 * @return string[]
452
 */
453
function wc_get_chosen_shipping_method_ids() {
454
	$method_ids     = array();
455
	$chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
456
	foreach ( $chosen_methods as $chosen_method ) {
0 ignored issues
show
Bug introduced by
The expression $chosen_methods of type array|string 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...
457
		$chosen_method = explode( ':', $chosen_method );
458
		$method_ids[]  = current( $chosen_method );
459
	}
460
	return $method_ids;
461
}
462
463
/**
464
 * Get cart cross sells.
465
 * @since 2.7.0
466
 * @return array
467
 */
468
function wc_get_cart_cross_sells() {
469
	$cross_sells = array();
470
	$in_cart     = array();
471
	if ( ! WC()->cart->is_empty() ) {
472
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
473
			if ( $values['quantity'] > 0 ) {
474
				$cross_sells = array_merge( $values['data']->get_cross_sells(), $cross_sells );
475
				$in_cart[] = $values['product_id'];
476
			}
477
		}
478
	}
479
	$cross_sells = array_diff( $cross_sells, $in_cart );
480
	return $cross_sells;
481
}
482
483
/**
484
 * See if we're displaying tax in the cart in a certain way.
485
 * @since 2.7.0
486
 * @return boolean
487
 */
488
function wc_cart_prices_include_tax() {
489
	return 'incl' === get_option( 'woocommerce_tax_display_cart', 'excl' );
490
}
491
492
/**
493
 * Determines the value that the customer spent and the subtotal
494
 * displayed, used for things like coupon validation.
495
 *
496
 * Since the coupon lines are displayed based on the TAX DISPLAY value
497
 * of cart, this is used to determine the spend.
498
 *
499
 * If cart totals are shown including tax, use the subtotal.
500
 * If cart totals are shown excluding tax, use the subtotal ex tax
501
 * (tax is shown after coupons).
502
 *
503
 * @since 2.7.0
504
 * @return string
505
 */
506
function wc_cart_subtotal_to_display() {
507
	return wc_format_decimal( WC()->cart->get_subtotal( wc_cart_prices_include_tax() ) );
508
}
509
510
/**
511
 * Gets the sub total (after calculation).
512
 *
513
 * @param bool $compound whether to include compound taxes
514
 * @return string formatted price
515
 */
516
function wc_cart_subtotal_html( $compound = false, $echo = true ) {
517
	// If the cart has compound tax, we want to show the subtotal as
518
	// cart + shipping + non-compound taxes (after discount)
519
	if ( $compound ) {
520
		$cart_subtotal = WC()->cart->cart_contents_total + WC()->cart->shipping_total + WC()->cart->get_taxes_total( false, false );
521
		$suffix = '';
522
523
	// Otherwise we show cart items totals only (before discount)
524
	} else {
525
		$cart_subtotal = wc_cart_subtotal_to_display();
526
527
		if ( wc_cart_prices_include_tax() ) {
528
			$suffix = wc_cart_prices_include_tax() ? '' : '<small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
529
		} else {
530
			$suffix = wc_cart_prices_include_tax() ? '<small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>' : '';
531
		}
532
	}
533
534
	$html = apply_filters( 'woocommerce_cart_subtotal', wc_price( $cart_subtotal ) . ' ' . $suffix, $compound, WC()->cart );
535
536
	if ( $echo ) {
537
		echo $html;
538
	} else {
539
		return $html;
540
	}
541
}
542
543
/**
544
 * Get the product row price per item.
545
 * @since 2.7.0
546
 * @param WC_Product $product
547
 * @param int $quantity to show
548
 * @return string formatted price
549
 */
550
function wc_cart_product_price_html( $product, $quantity = 1 ) {
551
	if ( wc_cart_prices_include_tax() ) {
552
		$price  = $product->get_price_including_tax( $quantity );
553
		$suffix = wc_cart_prices_include_tax() ? '' : '<small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
554
	} else {
555
		$price = $product->get_price_excluding_tax( $quantity );
556
		$suffix = wc_cart_prices_include_tax() ? '<small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>' : '';
557
	}
558
	return apply_filters( 'woocommerce_cart_product_price_html', wc_price( $product->is_taxable() ? $price . ' ' . $suffix : $price ), $product, $quantity );
559
}
560
561
/**
562
 * Should we show shipping in the cart?
563
 * @since 2.7.0
564
 * @return bool
565
 */
566
function wc_cart_show_shipping() {
567
	if ( ! wc_shipping_enabled() || WC()->cart->is_empty() ) {
568
		return false;
569
	}
570
571
	if ( 'yes' === get_option( 'woocommerce_shipping_cost_requires_address' ) && ! WC()->customer->has_calculated_shipping() && ! WC()->customer->has_shipping_address() ) {
572
		return false;
573
	}
574
575
	return apply_filters( 'woocommerce_cart_ready_to_calc_shipping', true );
576
}
577
578
/**
579
 * Load the persistent cart.
580
 */
581
function wc_load_persistent_cart() {
582
	if ( ! is_user_logged_in() ) {
583
		return;
584
	}
585
	$cart = WC()->session->get( 'cart', null );
586
587
	if ( is_null( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart', true ) ) ) {
588
		$cart          = array();
589
		$cart['items'] = $saved_cart;
590
		WC()->session->set( 'cart', $cart );
591
	}
592
}
593
add_action( 'wp_loaded', 'wc_load_persistent_cart', 5 );
594
595
/**
596
 * Update the persistent cart.
597
 */
598
function wc_update_persistent_cart() {
599
	update_user_meta( get_current_user_id(), '_woocommerce_persistent_cart', WC()->cart->get_cart_for_session() );
600
}
601
add_action( 'woocommerce_cart_updated', 'wc_update_persistent_cart' );
602
603
/**
604
 * Delete the persistent cart.
605
 */
606
function wc_delete_persistent_cart() {
607
	delete_user_meta( get_current_user_id(), '_woocommerce_persistent_cart' );
608
}
609
add_action( 'woocommerce_cart_emptied', 'wc_delete_persistent_cart' );
610
611
/**
612
 * Get chosen method for package from session.
613
 * @param  int $key
614
 * @param  array $package
615
 * @return string|bool
616
 */
617
function wc_get_chosen_shipping_method_for_package( $key, $package ) {
618
	$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
619
	$chosen_method  = isset( $chosen_methods[ $key ] ) ? $chosen_methods[ $key ] : false;
620
	$changed        = wc_shipping_methods_have_changed( $key, $package );
621
622
	// If not set, not available, or available methods have changed, set to the DEFAULT option
623
	if ( ! $chosen_method || $changed || ! isset( $package['rates'][ $chosen_method ] ) ) {
624
		$chosen_method          = wc_get_default_shipping_method_for_package( $key, $package, $chosen_method );
625
		$chosen_methods[ $key ] = $chosen_method;
626
		WC()->session->set( 'chosen_shipping_methods', $chosen_methods );
627
		do_action( 'woocommerce_shipping_method_chosen', $chosen_method );
628
	}
629
630
	return $chosen_method;
631
}
632
633
/**
634
 * Choose the default method for a package.
635
 * @param  string $key
636
 * @param  array $package
637
 * @return string
638
 */
639
function wc_get_default_shipping_method_for_package( $key, $package, $chosen_method ) {
0 ignored issues
show
Unused Code introduced by
The parameter $key 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...
640
	$rate_keys = array_keys( $package['rates'] );
641
	$default   = current( $rate_keys );
642
	$coupons   = WC()->cart->get_coupons();
643
644
	foreach ( $coupons as $coupon ) {
645
		if ( $coupon->get_free_shipping() ) {
646
			foreach ( $rate_keys as $rate_key ) {
647
				if ( 0 === stripos( $rate_key, 'free_shipping' ) ) {
648
					$default = $rate_key;
649
					break;
650
				}
651
			}
652
			break;
653
		}
654
	}
655
656
	return apply_filters( 'woocommerce_shipping_chosen_method', $default, $package['rates'], $chosen_method );
657
}
658
659
/**
660
 * See if the methods have changed since the last request.
661
 * @param  int $key
662
 * @param  array $package
663
 * @return bool
664
 */
665
function wc_shipping_methods_have_changed( $key, $package ) {
666
	// Lookup previous methods from session.
667
	$previous_shipping_methods = WC()->session->get( 'previous_shipping_methods' );
668
669
	// Get new and old rates.
670
	$new_rates  = array_keys( $package['rates'] );
671
	$prev_rates = isset( $previous_shipping_methods[ $key ] ) ? $previous_shipping_methods[ $key ] : false;
672
673
	// Update session.
674
	$previous_shipping_methods[ $key ] = $new_rates;
675
	WC()->session->set( 'previous_shipping_methods', $previous_shipping_methods );
676
677
	return $new_rates != $prev_rates;
678
}
679