Issues (942)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/wc-cart-functions.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * WooCommerce Cart Functions
4
 *
5
 * Functions for cart specific things.
6
 *
7
 * @package WooCommerce/Functions
8
 * @version 2.5.0
9
 */
10
11
defined( 'ABSPATH' ) || exit;
12
13
/**
14
 * Prevent password protected products being added to the cart.
15
 *
16
 * @param  bool $passed     Validation.
17
 * @param  int  $product_id Product ID.
18
 * @return bool
19
 */
20
function wc_protected_product_add_to_cart( $passed, $product_id ) {
21
	if ( post_password_required( $product_id ) ) {
22
		$passed = false;
23
		wc_add_notice( __( 'This product is protected and cannot be purchased.', 'woocommerce' ), 'error' );
24
	}
25
	return $passed;
26
}
27
add_filter( 'woocommerce_add_to_cart_validation', 'wc_protected_product_add_to_cart', 10, 2 );
28
29
/**
30
 * Clears the cart session when called.
31
 */
32
function wc_empty_cart() {
33 1
	if ( ! isset( WC()->cart ) || '' === WC()->cart ) {
34
		WC()->cart = new WC_Cart();
35
	}
36 1
	WC()->cart->empty_cart( false );
37
}
38
39
/**
40
 * Load the persistent cart.
41
 *
42
 * @param string  $user_login User login.
43
 * @param WP_User $user       User data.
44
 * @deprecated 2.3
45
 */
46
function wc_load_persistent_cart( $user_login, $user ) {
47
	if ( ! $user || ! apply_filters( 'woocommerce_persistent_cart_enabled', true ) ) {
48
		return;
49
	}
50
51
	$saved_cart = get_user_meta( $user->ID, '_woocommerce_persistent_cart_' . get_current_blog_id(), true );
52
53
	if ( ! $saved_cart ) {
54
		return;
55
	}
56
57
	$cart = WC()->session->cart;
58
59
	if ( empty( $cart ) || ! is_array( $cart ) || 0 === count( $cart ) ) {
60
		WC()->session->cart = $saved_cart['cart'];
61
	}
62
}
63
64
/**
65
 * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
66
 *
67
 * Do not use for redirects, use {@see wp_get_referer()} instead.
68
 *
69
 * @since 2.6.1
70
 * @return string|false Referer URL on success, false on failure.
71
 */
72
function wc_get_raw_referer() {
73
	if ( function_exists( 'wp_get_raw_referer' ) ) {
74
		return wp_get_raw_referer();
75
	}
76
77
	if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) { // WPCS: input var ok, CSRF ok.
78
		return wp_unslash( $_REQUEST['_wp_http_referer'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
79
	} elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { // WPCS: input var ok, CSRF ok.
80
		return wp_unslash( $_SERVER['HTTP_REFERER'] ); // WPCS: input var ok, CSRF ok, sanitization ok.
81
	}
82
83
	return false;
84
}
85
86
/**
87
 * Add to cart messages.
88
 *
89
 * @param int|array $products Product ID list or single product ID.
90
 * @param bool      $show_qty Should qty's be shown? Added in 2.6.0.
91
 * @param bool      $return   Return message rather than add it.
92
 *
93
 * @return mixed
94
 */
95
function wc_add_to_cart_message( $products, $show_qty = false, $return = false ) {
96 1
	$titles = array();
97 1
	$count  = 0;
98
99 1
	if ( ! is_array( $products ) ) {
100 1
		$products = array( $products => 1 );
101 1
		$show_qty = false;
102
	}
103
104 1
	if ( ! $show_qty ) {
105 1
		$products = array_fill_keys( array_keys( $products ), 1 );
106
	}
107
108 1
	foreach ( $products as $product_id => $qty ) {
109
		/* translators: %s: product name */
110 1
		$titles[] = apply_filters( 'woocommerce_add_to_cart_qty_html', ( $qty > 1 ? absint( $qty ) . ' &times; ' : '' ), $product_id ) . apply_filters( 'woocommerce_add_to_cart_item_name_in_quotes', sprintf( _x( '&ldquo;%s&rdquo;', 'Item name in quotes', 'woocommerce' ), strip_tags( get_the_title( $product_id ) ) ), $product_id );
111 1
		$count   += $qty;
112
	}
113
114 1
	$titles = array_filter( $titles );
115
	/* translators: %s: product name */
116 1
	$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 ) );
117
118
	// Output success messages.
119 1
	if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
120
		$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' ) );
121
		$message   = sprintf( '<a href="%s" tabindex="1" class="button wc-forward">%s</a> %s', esc_url( $return_to ), esc_html__( 'Continue shopping', 'woocommerce' ), esc_html( $added_text ) );
122
	} else {
123 1
		$message = sprintf( '<a href="%s" tabindex="1" class="button wc-forward">%s</a> %s', esc_url( wc_get_cart_url() ), esc_html__( 'View cart', 'woocommerce' ), esc_html( $added_text ) );
124
	}
125
126 1
	if ( has_filter( 'wc_add_to_cart_message' ) ) {
127
		wc_deprecated_function( 'The wc_add_to_cart_message filter', '3.0', 'wc_add_to_cart_message_html' );
128
		$message = apply_filters( 'wc_add_to_cart_message', $message, $product_id );
129
	}
130
131 1
	$message = apply_filters( 'wc_add_to_cart_message_html', $message, $products, $show_qty );
132
133 1
	if ( $return ) {
134 1
		return $message;
135
	} else {
136
		wc_add_notice( $message, apply_filters( 'woocommerce_add_to_cart_notice_type', 'success' ) );
137
	}
138
}
139
140
/**
141
 * Comma separate a list of item names, and replace final comma with 'and'.
142
 *
143
 * @param  array $items Cart items.
144
 * @return string
145
 */
146
function wc_format_list_of_items( $items ) {
147 2
	$item_string = '';
148
149 2
	foreach ( $items as $key => $item ) {
150 2
		$item_string .= $item;
151
152 2
		if ( count( $items ) === $key + 2 ) {
153 1
			$item_string .= ' ' . __( 'and', 'woocommerce' ) . ' ';
154 2
		} elseif ( count( $items ) !== $key + 1 ) {
155
			$item_string .= ', ';
156
		}
157
	}
158
159 2
	return $item_string;
160
}
161
162
/**
163
 * Clear cart after payment.
164
 */
165
function wc_clear_cart_after_payment() {
166
	global $wp;
167
168
	if ( ! empty( $wp->query_vars['order-received'] ) ) {
169
170
		$order_id  = absint( $wp->query_vars['order-received'] );
171
		$order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok.
172
173
		if ( $order_id > 0 ) {
174
			$order = wc_get_order( $order_id );
175
176
			if ( $order && hash_equals( $order->get_order_key(), $order_key ) ) {
177
				WC()->cart->empty_cart();
178
			}
179
		}
180
	}
181
182
	if ( WC()->session->order_awaiting_payment > 0 ) {
183
		$order = wc_get_order( WC()->session->order_awaiting_payment );
184
185
		if ( $order && $order->get_id() > 0 ) {
186
			// If the order has not failed, or is not pending, the order must have gone through.
187
			if ( ! $order->has_status( array( 'failed', 'pending', 'cancelled' ) ) ) {
188
				WC()->cart->empty_cart();
189
			}
190
		}
191
	}
192
}
193
add_action( 'get_header', 'wc_clear_cart_after_payment' );
194
195
/**
196
 * Get the subtotal.
197
 */
198
function wc_cart_totals_subtotal_html() {
199 1
	echo WC()->cart->get_cart_subtotal(); // WPCS: XSS ok.
200
}
201
202
/**
203
 * Get shipping methods.
204
 */
205
function wc_cart_totals_shipping_html() {
206
	$packages           = WC()->shipping()->get_packages();
207
	$first              = true;
208
209
	foreach ( $packages as $i => $package ) {
210
		$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
211
		$product_names = array();
212
213
		if ( count( $packages ) > 1 ) {
214
			foreach ( $package['contents'] as $item_id => $values ) {
215
				$product_names[ $item_id ] = $values['data']->get_name() . ' &times;' . $values['quantity'];
216
			}
217
			$product_names = apply_filters( 'woocommerce_shipping_package_details_array', $product_names, $package );
218
		}
219
220
		wc_get_template(
221
			'cart/cart-shipping.php',
222
			array(
223
				'package'                  => $package,
224
				'available_methods'        => $package['rates'],
225
				'show_package_details'     => count( $packages ) > 1,
226
				'show_shipping_calculator' => is_cart() && apply_filters( 'woocommerce_shipping_show_shipping_calculator', $first, $i, $package ),
227
				'package_details'          => implode( ', ', $product_names ),
228
				/* translators: %d: shipping package number */
229
				'package_name'             => apply_filters( 'woocommerce_shipping_package_name', ( ( $i + 1 ) > 1 ) ? sprintf( _x( 'Shipping %d', 'shipping packages', 'woocommerce' ), ( $i + 1 ) ) : _x( 'Shipping', 'shipping packages', 'woocommerce' ), $i, $package ),
230
				'index'                    => $i,
231
				'chosen_method'            => $chosen_method,
232
				'formatted_destination'    => WC()->countries->get_formatted_address( $package['destination'], ', ' ),
233
				'has_calculated_shipping'  => WC()->customer->has_calculated_shipping(),
234
			)
235
		);
236
237
		$first = false;
238
	}
239
}
240
241
/**
242
 * Get taxes total.
243
 */
244
function wc_cart_totals_taxes_total_html() {
245
	echo apply_filters( 'woocommerce_cart_totals_taxes_total_html', wc_price( WC()->cart->get_taxes_total() ) ); // WPCS: XSS ok.
246
}
247
248
/**
249
 * Get a coupon label.
250
 *
251
 * @param string|WC_Coupon $coupon Coupon data or code.
252
 * @param bool             $echo   Echo or return.
253
 *
254
 * @return string
255
 */
256
function wc_cart_totals_coupon_label( $coupon, $echo = true ) {
257 1
	if ( is_string( $coupon ) ) {
258
		$coupon = new WC_Coupon( $coupon );
259
	}
260
261
	/* translators: %s: coupon code */
262 1
	$label = apply_filters( 'woocommerce_cart_totals_coupon_label', sprintf( esc_html__( 'Coupon: %s', 'woocommerce' ), $coupon->get_code() ), $coupon );
263
264 1
	if ( $echo ) {
265 1
		echo $label; // WPCS: XSS ok.
266
	} else {
267
		return $label;
268
	}
269
}
270
271
/**
272
 * Get coupon display HTML.
273
 *
274
 * @param string|WC_Coupon $coupon Coupon data or code.
275
 */
276
function wc_cart_totals_coupon_html( $coupon ) {
277
	if ( is_string( $coupon ) ) {
278
		$coupon = new WC_Coupon( $coupon );
279
	}
280
281
	$discount_amount_html = '';
282
283
	$amount               = WC()->cart->get_coupon_discount_amount( $coupon->get_code(), WC()->cart->display_cart_ex_tax );
284
	$discount_amount_html = '-' . wc_price( $amount );
285
286
	if ( $coupon->get_free_shipping() && empty( $amount ) ) {
287
		$discount_amount_html = __( 'Free shipping coupon', 'woocommerce' );
288
	}
289
290
	$discount_amount_html = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_amount_html, $coupon );
291
	$coupon_html          = $discount_amount_html . ' <a href="' . esc_url( add_query_arg( 'remove_coupon', rawurlencode( $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>';
292
293
	echo wp_kses( apply_filters( 'woocommerce_cart_totals_coupon_html', $coupon_html, $coupon, $discount_amount_html ), array_replace_recursive( wp_kses_allowed_html( 'post' ), array( 'a' => array( 'data-coupon' => true ) ) ) ); // phpcs:ignore PHPCompatibility.PHP.NewFunctions.array_replace_recursiveFound
294
}
295
296
/**
297
 * Get order total html including inc tax if needed.
298
 */
299
function wc_cart_totals_order_total_html() {
300
	$value = '<strong>' . WC()->cart->get_total() . '</strong> ';
301
302
	// If prices are tax inclusive, show taxes here.
303
	if ( wc_tax_enabled() && WC()->cart->display_prices_including_tax() ) {
304
		$tax_string_array = array();
305
		$cart_tax_totals  = WC()->cart->get_tax_totals();
306
307
		if ( get_option( 'woocommerce_tax_total_display' ) === 'itemized' ) {
308
			foreach ( $cart_tax_totals as $code => $tax ) {
309
				$tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label );
310
			}
311
		} elseif ( ! empty( $cart_tax_totals ) ) {
312
			$tax_string_array[] = sprintf( '%s %s', wc_price( WC()->cart->get_taxes_total( true, true ) ), WC()->countries->tax_or_vat() );
313
		}
314
315
		if ( ! empty( $tax_string_array ) ) {
316
			$taxable_address = WC()->customer->get_taxable_address();
317
			/* translators: %s: country name */
318
			$estimated_text = WC()->customer->is_customer_outside_base() && ! WC()->customer->has_calculated_shipping() ? sprintf( ' ' . __( 'estimated for %s', 'woocommerce' ), WC()->countries->estimated_for_prefix( $taxable_address[0] ) . WC()->countries->countries[ $taxable_address[0] ] ) : '';
319
			/* translators: %s: tax information */
320
			$value .= '<small class="includes_tax">' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) . $estimated_text ) . '</small>';
321
		}
322
	}
323
324
	echo apply_filters( 'woocommerce_cart_totals_order_total_html', $value ); // WPCS: XSS ok.
325
}
326
327
/**
328
 * Get the fee value.
329
 *
330
 * @param object $fee Fee data.
331
 */
332
function wc_cart_totals_fee_html( $fee ) {
333
	$cart_totals_fee_html = WC()->cart->display_prices_including_tax() ? wc_price( $fee->total + $fee->tax ) : wc_price( $fee->total );
334
335
	echo apply_filters( 'woocommerce_cart_totals_fee_html', $cart_totals_fee_html, $fee ); // WPCS: XSS ok.
336
}
337
338
/**
339
 * Get a shipping methods full label including price.
340
 *
341
 * @param  WC_Shipping_Rate $method Shipping method rate data.
342
 * @return string
343
 */
344
function wc_cart_totals_shipping_method_label( $method ) {
345
	$label     = $method->get_label();
346
	$has_cost  = 0 < $method->cost;
347
	$hide_cost = ! $has_cost && in_array( $method->get_method_id(), array( 'free_shipping', 'local_pickup' ), true );
348
349
	if ( $has_cost && ! $hide_cost ) {
350
		if ( WC()->cart->display_prices_including_tax() ) {
351
			$label .= ': ' . wc_price( $method->cost + $method->get_shipping_tax() );
0 ignored issues
show
$method->cost + $method->get_shipping_tax() is of type array, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
352
			if ( $method->get_shipping_tax() > 0 && ! wc_prices_include_tax() ) {
353
				$label .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
354
			}
355
		} else {
356
			$label .= ': ' . wc_price( $method->cost );
357
			if ( $method->get_shipping_tax() > 0 && wc_prices_include_tax() ) {
358
				$label .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
359
			}
360
		}
361
	}
362
363
	return apply_filters( 'woocommerce_cart_shipping_method_full_label', $label, $method );
364
}
365
366
/**
367
 * Round discount.
368
 *
369
 * @param  double $value Amount to round.
370
 * @param  int    $precision DP to round.
371
 * @return float
372
 */
373
function wc_cart_round_discount( $value, $precision ) {
374 82
	return wc_round_discount( $value, $precision );
375
}
376
377
/**
378
 * Gets chosen shipping method IDs from chosen_shipping_methods session, without instance IDs.
379
 *
380
 * @since  2.6.2
381
 * @return string[]
382
 */
383
function wc_get_chosen_shipping_method_ids() {
384 95
	$method_ids     = array();
385 95
	$chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
386 95
	foreach ( $chosen_methods as $chosen_method ) {
387 87
		$chosen_method = explode( ':', $chosen_method );
388 87
		$method_ids[]  = current( $chosen_method );
389
	}
390 95
	return $method_ids;
391
}
392
393
/**
394
 * Get chosen method for package from session.
395
 *
396
 * @since  3.2.0
397
 * @param  int   $key Key of package.
398
 * @param  array $package Package data array.
399
 * @return string|bool
400
 */
401
function wc_get_chosen_shipping_method_for_package( $key, $package ) {
402 17
	$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
403 17
	$chosen_method  = isset( $chosen_methods[ $key ] ) ? $chosen_methods[ $key ] : false;
404 17
	$changed        = wc_shipping_methods_have_changed( $key, $package );
405
406
	// This is deprecated but here for BW compat. TODO: Remove in 4.0.0.
407 17
	$method_counts = WC()->session->get( 'shipping_method_counts' );
408
409 17
	if ( ! empty( $method_counts[ $key ] ) ) {
410 14
		$method_count = absint( $method_counts[ $key ] );
411
	} else {
412 11
		$method_count = 0;
413
	}
414
415
	// If not set, not available, or available methods have changed, set to the DEFAULT option.
416 17
	if ( ! $chosen_method || $changed || ! isset( $package['rates'][ $chosen_method ] ) || count( $package['rates'] ) !== $method_count ) {
417 17
		$chosen_method          = wc_get_default_shipping_method_for_package( $key, $package, $chosen_method );
418 17
		$chosen_methods[ $key ] = $chosen_method;
419 17
		$method_counts[ $key ]  = count( $package['rates'] );
420
421 17
		WC()->session->set( 'chosen_shipping_methods', $chosen_methods );
422 17
		WC()->session->set( 'shipping_method_counts', $method_counts );
423
424 17
		do_action( 'woocommerce_shipping_method_chosen', $chosen_method );
425
	}
426 17
	return $chosen_method;
427
}
428
429
/**
430
 * Choose the default method for a package.
431
 *
432
 * @since  3.2.0
433
 * @param  int    $key Key of package.
434
 * @param  array  $package Package data array.
435
 * @param  string $chosen_method Chosen method id.
436
 * @return string
437
 */
438
function wc_get_default_shipping_method_for_package( $key, $package, $chosen_method ) {
439 17
	$rate_keys = array_keys( $package['rates'] );
440 17
	$default   = current( $rate_keys );
441 17
	$coupons   = WC()->cart->get_coupons();
442 17
	foreach ( $coupons as $coupon ) {
443 4
		if ( $coupon->get_free_shipping() ) {
444
			foreach ( $rate_keys as $rate_key ) {
445
				if ( 0 === stripos( $rate_key, 'free_shipping' ) ) {
446
					$default = $rate_key;
447
					break;
448
				}
449
			}
450
			break;
451
		}
452
	}
453 17
	return apply_filters( 'woocommerce_shipping_chosen_method', $default, $package['rates'], $chosen_method );
454
}
455
456
/**
457
 * See if the methods have changed since the last request.
458
 *
459
 * @since  3.2.0
460
 * @param  int   $key Key of package.
461
 * @param  array $package Package data array.
462
 * @return bool
463
 */
464
function wc_shipping_methods_have_changed( $key, $package ) {
465
	// Lookup previous methods from session.
466 17
	$previous_shipping_methods = WC()->session->get( 'previous_shipping_methods' );
467
	// Get new and old rates.
468 17
	$new_rates  = array_keys( $package['rates'] );
469 17
	$prev_rates = isset( $previous_shipping_methods[ $key ] ) ? $previous_shipping_methods[ $key ] : false;
470
	// Update session.
471 17
	$previous_shipping_methods[ $key ] = $new_rates;
472 17
	WC()->session->set( 'previous_shipping_methods', $previous_shipping_methods );
473 17
	return $new_rates !== $prev_rates;
474
}
475
476
/**
477
 * Gets a hash of important product data that when changed should cause cart items to be invalidated.
478
 *
479
 * The woocommerce_cart_item_data_to_validate filter can be used to add custom properties.
480
 *
481
 * @param WC_Product $product Product object.
482
 * @return string
483
 */
484
function wc_get_cart_item_data_hash( $product ) {
485 82
	return md5(
486 82
		wp_json_encode(
487 82
			apply_filters(
488 82
				'woocommerce_cart_item_data_to_validate',
489
				array(
490 82
					'type'       => $product->get_type(),
491 82
					'attributes' => 'variation' === $product->get_type() ? $product->get_variation_attributes() : '',
492
				),
493 82
				$product
494
			)
495
		)
496
	);
497
}
498