Completed
Pull Request — master (#11889)
by Mike
12:12
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_prices_include_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 = false ) {
441
	if ( ! $precision ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $precision of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

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