Completed
Push — master ( deec93...29d630 )
by Claudio
31:19 queued 22:42
created

wc-core-functions.php ➔ wc_load_cart()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * WooCommerce Core Functions
4
 *
5
 * General core functions available on both the front-end and admin.
6
 *
7
 * @package WooCommerce\Functions
8
 * @version 3.3.0
9
 */
10
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
// Include core functions (available in both admin and frontend).
16
require WC_ABSPATH . 'includes/wc-conditional-functions.php';
17
require WC_ABSPATH . 'includes/wc-coupon-functions.php';
18
require WC_ABSPATH . 'includes/wc-user-functions.php';
19
require WC_ABSPATH . 'includes/wc-deprecated-functions.php';
20
require WC_ABSPATH . 'includes/wc-formatting-functions.php';
21
require WC_ABSPATH . 'includes/wc-order-functions.php';
22
require WC_ABSPATH . 'includes/wc-order-item-functions.php';
23
require WC_ABSPATH . 'includes/wc-page-functions.php';
24
require WC_ABSPATH . 'includes/wc-product-functions.php';
25
require WC_ABSPATH . 'includes/wc-stock-functions.php';
26
require WC_ABSPATH . 'includes/wc-account-functions.php';
27
require WC_ABSPATH . 'includes/wc-term-functions.php';
28
require WC_ABSPATH . 'includes/wc-attribute-functions.php';
29
require WC_ABSPATH . 'includes/wc-rest-functions.php';
30
require WC_ABSPATH . 'includes/wc-widget-functions.php';
31
require WC_ABSPATH . 'includes/wc-webhook-functions.php';
32
33
/**
34
 * Filters on data used in admin and frontend.
35
 */
36
add_filter( 'woocommerce_coupon_code', 'html_entity_decode' );
37
add_filter( 'woocommerce_coupon_code', 'wc_sanitize_coupon_code' );
38
add_filter( 'woocommerce_coupon_code', 'wc_strtolower' );
39
add_filter( 'woocommerce_stock_amount', 'intval' ); // Stock amounts are integers by default.
40
add_filter( 'woocommerce_shipping_rate_label', 'sanitize_text_field' ); // Shipping rate label.
41
add_filter( 'woocommerce_attribute_label', 'wp_kses_post', 100 );
42
43
/**
44
 * Short Description (excerpt).
45
 */
46
if ( function_exists( 'do_blocks' ) ) {
47
	add_filter( 'woocommerce_short_description', 'do_blocks', 9 );
48
}
49
add_filter( 'woocommerce_short_description', 'wptexturize' );
50
add_filter( 'woocommerce_short_description', 'convert_smilies' );
51
add_filter( 'woocommerce_short_description', 'convert_chars' );
52
add_filter( 'woocommerce_short_description', 'wpautop' );
53
add_filter( 'woocommerce_short_description', 'shortcode_unautop' );
54
add_filter( 'woocommerce_short_description', 'prepend_attachment' );
55
add_filter( 'woocommerce_short_description', 'do_shortcode', 11 ); // After wpautop().
56
add_filter( 'woocommerce_short_description', 'wc_format_product_short_description', 9999999 );
57
add_filter( 'woocommerce_short_description', 'wc_do_oembeds' );
58
add_filter( 'woocommerce_short_description', array( $GLOBALS['wp_embed'], 'run_shortcode' ), 8 ); // Before wpautop().
59
60
/**
61
 * Define a constant if it is not already defined.
62
 *
63
 * @since 3.0.0
64
 * @param string $name  Constant name.
65
 * @param mixed  $value Value.
66
 */
67
function wc_maybe_define_constant( $name, $value ) {
68 4
	if ( ! defined( $name ) ) {
69 1
		define( $name, $value );
70
	}
71
}
72
73
/**
74
 * Create a new order programmatically.
75
 *
76
 * Returns a new order object on success which can then be used to add additional data.
77
 *
78
 * @param  array $args Order arguments.
79
 * @return WC_Order|WP_Error
80
 */
81
function wc_create_order( $args = array() ) {
82
	$default_args = array(
83 96
		'status'        => null,
84
		'customer_id'   => null,
85
		'customer_note' => null,
86
		'parent'        => null,
87
		'created_via'   => null,
88
		'cart_hash'     => null,
89
		'order_id'      => 0,
90
	);
91
92
	try {
93 96
		$args  = wp_parse_args( $args, $default_args );
94 96
		$order = new WC_Order( $args['order_id'] );
95
96
		// Update props that were set (not null).
97 96
		if ( ! is_null( $args['parent'] ) ) {
98
			$order->set_parent_id( absint( $args['parent'] ) );
99
		}
100
101 96
		if ( ! is_null( $args['status'] ) ) {
102 96
			$order->set_status( $args['status'] );
103
		}
104
105 96
		if ( ! is_null( $args['customer_note'] ) ) {
106 95
			$order->set_customer_note( $args['customer_note'] );
107
		}
108
109 96
		if ( ! is_null( $args['customer_id'] ) ) {
110 96
			$order->set_customer_id( is_numeric( $args['customer_id'] ) ? absint( $args['customer_id'] ) : 0 );
111
		}
112
113 96
		if ( ! is_null( $args['created_via'] ) ) {
114 1
			$order->set_created_via( sanitize_text_field( $args['created_via'] ) );
115
		}
116
117 96
		if ( ! is_null( $args['cart_hash'] ) ) {
118 1
			$order->set_cart_hash( sanitize_text_field( $args['cart_hash'] ) );
119
		}
120
121
		// Set these fields when creating a new order but not when updating an existing order.
122 96
		if ( ! $args['order_id'] ) {
123 96
			$order->set_currency( get_woocommerce_currency() );
124 96
			$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
125 96
			$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
126 96
			$order->set_customer_user_agent( wc_get_user_agent() );
127
		}
128
129
		// Update other order props set automatically.
130 96
		$order->save();
131
	} catch ( Exception $e ) {
132
		return new WP_Error( 'error', $e->getMessage() );
133
	}
134
135 96
	return $order;
136
}
137
138
/**
139
 * Update an order. Uses wc_create_order.
140
 *
141
 * @param  array $args Order arguments.
142
 * @return WC_Order|WP_Error
143
 */
144
function wc_update_order( $args ) {
145 1
	if ( empty( $args['order_id'] ) ) {
146 1
		return new WP_Error( __( 'Invalid order ID.', 'woocommerce' ) );
147
	}
148 1
	return wc_create_order( $args );
149
}
150
151
/**
152
 * Get template part (for templates like the shop-loop).
153
 *
154
 * WC_TEMPLATE_DEBUG_MODE will prevent overrides in themes from taking priority.
155
 *
156
 * @param mixed  $slug Template slug.
157
 * @param string $name Template name (default: '').
158
 */
159
function wc_get_template_part( $slug, $name = '' ) {
160 1
	$cache_key = sanitize_key( implode( '-', array( 'template-part', $slug, $name, WC_VERSION ) ) );
161 1
	$template  = (string) wp_cache_get( $cache_key, 'woocommerce' );
162
163 1
	if ( ! $template ) {
164 1
		if ( $name ) {
165
			$template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template(
166
				array(
167
					"{$slug}-{$name}.php",
168
					WC()->template_path() . "{$slug}-{$name}.php",
169
				)
170
			);
171
172
			if ( ! $template ) {
173
				$fallback = WC()->plugin_path() . "/templates/{$slug}-{$name}.php";
174
				$template = file_exists( $fallback ) ? $fallback : '';
175
			}
176
		}
177
178 1
		if ( ! $template ) {
179
			// If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php.
180 1
			$template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template(
181
				array(
182 1
					"{$slug}.php",
183 1
					WC()->template_path() . "{$slug}.php",
184
				)
185
			);
186
		}
187
188 1
		wp_cache_set( $cache_key, $template, 'woocommerce' );
189
	}
190
191
	// Allow 3rd party plugins to filter template file from their plugin.
192 1
	$template = apply_filters( 'wc_get_template_part', $template, $slug, $name );
193
194 1
	if ( $template ) {
195
		load_template( $template, false );
196
	}
197
}
198
199
/**
200
 * Get other templates (e.g. product attributes) passing attributes and including the file.
201
 *
202
 * @param string $template_name Template name.
203
 * @param array  $args          Arguments. (default: array).
204
 * @param string $template_path Template path. (default: '').
205
 * @param string $default_path  Default path. (default: '').
206
 */
207
function wc_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
208 7
	$cache_key = sanitize_key( implode( '-', array( 'template', $template_name, $template_path, $default_path, WC_VERSION ) ) );
209 7
	$template  = (string) wp_cache_get( $cache_key, 'woocommerce' );
210
211 7
	if ( ! $template ) {
212 7
		$template = wc_locate_template( $template_name, $template_path, $default_path );
213 7
		wp_cache_set( $cache_key, $template, 'woocommerce' );
214
	}
215
216
	// Allow 3rd party plugin filter template file from their plugin.
217 7
	$filter_template = apply_filters( 'wc_get_template', $template, $template_name, $args, $template_path, $default_path );
218
219 7
	if ( $filter_template !== $template ) {
220
		if ( ! file_exists( $filter_template ) ) {
221
			/* translators: %s template */
222
			wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '<code>' . $template . '</code>' ), '2.1' );
223
			return;
224
		}
225
		$template = $filter_template;
226
	}
227
228
	$action_args = array(
229 7
		'template_name' => $template_name,
230 7
		'template_path' => $template_path,
231 7
		'located'       => $template,
232 7
		'args'          => $args,
233
	);
234
235 7
	if ( ! empty( $args ) && is_array( $args ) ) {
236 7
		if ( isset( $args['action_args'] ) ) {
237 1
			wc_doing_it_wrong(
238 1
				__FUNCTION__,
239 1
				__( 'action_args should not be overwritten when calling wc_get_template.', 'woocommerce' ),
240 1
				'3.6.0'
241
			);
242 1
			unset( $args['action_args'] );
243
		}
244 7
		extract( $args ); // @codingStandardsIgnoreLine
245
	}
246
247 7
	do_action( 'woocommerce_before_template_part', $action_args['template_name'], $action_args['template_path'], $action_args['located'], $action_args['args'] );
248
249 7
	include $action_args['located'];
250
251 7
	do_action( 'woocommerce_after_template_part', $action_args['template_name'], $action_args['template_path'], $action_args['located'], $action_args['args'] );
252
}
253
254
/**
255
 * Like wc_get_template, but returns the HTML instead of outputting.
256
 *
257
 * @see wc_get_template
258
 * @since 2.5.0
259
 * @param string $template_name Template name.
260
 * @param array  $args          Arguments. (default: array).
261
 * @param string $template_path Template path. (default: '').
262
 * @param string $default_path  Default path. (default: '').
263
 *
264
 * @return string
265
 */
266
function wc_get_template_html( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
267 1
	ob_start();
268 1
	wc_get_template( $template_name, $args, $template_path, $default_path );
269 1
	return ob_get_clean();
270
}
271
/**
272
 * Locate a template and return the path for inclusion.
273
 *
274
 * This is the load order:
275
 *
276
 * yourtheme/$template_path/$template_name
277
 * yourtheme/$template_name
278
 * $default_path/$template_name
279
 *
280
 * @param string $template_name Template name.
281
 * @param string $template_path Template path. (default: '').
282
 * @param string $default_path  Default path. (default: '').
283
 * @return string
284
 */
285
function wc_locate_template( $template_name, $template_path = '', $default_path = '' ) {
286 7
	if ( ! $template_path ) {
287 7
		$template_path = WC()->template_path();
288
	}
289
290 7
	if ( ! $default_path ) {
291 7
		$default_path = WC()->plugin_path() . '/templates/';
292
	}
293
294
	// Look within passed path within the theme - this is priority.
295 7
	$template = locate_template(
296
		array(
297 7
			trailingslashit( $template_path ) . $template_name,
298 7
			$template_name,
299
		)
300
	);
301
302
	// Get default template/.
303 7
	if ( ! $template || WC_TEMPLATE_DEBUG_MODE ) {
304 7
		$template = $default_path . $template_name;
305
	}
306
307
	// Return what we found.
308 7
	return apply_filters( 'woocommerce_locate_template', $template, $template_name, $template_path );
309
}
310
311
/**
312
 * Get Base Currency Code.
313
 *
314
 * @return string
315
 */
316
function get_woocommerce_currency() {
317 572
	return apply_filters( 'woocommerce_currency', get_option( 'woocommerce_currency' ) );
318
}
319
320
/**
321
 * Get full list of currency codes.
322
 *
323
 * @return array
324
 */
325
function get_woocommerce_currencies() {
326 560
	static $currencies;
327
328 560
	if ( ! isset( $currencies ) ) {
329
		$currencies = array_unique(
330
			apply_filters(
331
				'woocommerce_currencies',
332
				array(
333
					'AED' => __( 'United Arab Emirates dirham', 'woocommerce' ),
334
					'AFN' => __( 'Afghan afghani', 'woocommerce' ),
335
					'ALL' => __( 'Albanian lek', 'woocommerce' ),
336
					'AMD' => __( 'Armenian dram', 'woocommerce' ),
337
					'ANG' => __( 'Netherlands Antillean guilder', 'woocommerce' ),
338
					'AOA' => __( 'Angolan kwanza', 'woocommerce' ),
339
					'ARS' => __( 'Argentine peso', 'woocommerce' ),
340
					'AUD' => __( 'Australian dollar', 'woocommerce' ),
341
					'AWG' => __( 'Aruban florin', 'woocommerce' ),
342
					'AZN' => __( 'Azerbaijani manat', 'woocommerce' ),
343
					'BAM' => __( 'Bosnia and Herzegovina convertible mark', 'woocommerce' ),
344
					'BBD' => __( 'Barbadian dollar', 'woocommerce' ),
345
					'BDT' => __( 'Bangladeshi taka', 'woocommerce' ),
346
					'BGN' => __( 'Bulgarian lev', 'woocommerce' ),
347
					'BHD' => __( 'Bahraini dinar', 'woocommerce' ),
348
					'BIF' => __( 'Burundian franc', 'woocommerce' ),
349
					'BMD' => __( 'Bermudian dollar', 'woocommerce' ),
350
					'BND' => __( 'Brunei dollar', 'woocommerce' ),
351
					'BOB' => __( 'Bolivian boliviano', 'woocommerce' ),
352
					'BRL' => __( 'Brazilian real', 'woocommerce' ),
353
					'BSD' => __( 'Bahamian dollar', 'woocommerce' ),
354
					'BTC' => __( 'Bitcoin', 'woocommerce' ),
355
					'BTN' => __( 'Bhutanese ngultrum', 'woocommerce' ),
356
					'BWP' => __( 'Botswana pula', 'woocommerce' ),
357
					'BYR' => __( 'Belarusian ruble (old)', 'woocommerce' ),
358
					'BYN' => __( 'Belarusian ruble', 'woocommerce' ),
359
					'BZD' => __( 'Belize dollar', 'woocommerce' ),
360
					'CAD' => __( 'Canadian dollar', 'woocommerce' ),
361
					'CDF' => __( 'Congolese franc', 'woocommerce' ),
362
					'CHF' => __( 'Swiss franc', 'woocommerce' ),
363
					'CLP' => __( 'Chilean peso', 'woocommerce' ),
364
					'CNY' => __( 'Chinese yuan', 'woocommerce' ),
365
					'COP' => __( 'Colombian peso', 'woocommerce' ),
366
					'CRC' => __( 'Costa Rican col&oacute;n', 'woocommerce' ),
367
					'CUC' => __( 'Cuban convertible peso', 'woocommerce' ),
368
					'CUP' => __( 'Cuban peso', 'woocommerce' ),
369
					'CVE' => __( 'Cape Verdean escudo', 'woocommerce' ),
370
					'CZK' => __( 'Czech koruna', 'woocommerce' ),
371
					'DJF' => __( 'Djiboutian franc', 'woocommerce' ),
372
					'DKK' => __( 'Danish krone', 'woocommerce' ),
373
					'DOP' => __( 'Dominican peso', 'woocommerce' ),
374
					'DZD' => __( 'Algerian dinar', 'woocommerce' ),
375
					'EGP' => __( 'Egyptian pound', 'woocommerce' ),
376
					'ERN' => __( 'Eritrean nakfa', 'woocommerce' ),
377
					'ETB' => __( 'Ethiopian birr', 'woocommerce' ),
378
					'EUR' => __( 'Euro', 'woocommerce' ),
379
					'FJD' => __( 'Fijian dollar', 'woocommerce' ),
380
					'FKP' => __( 'Falkland Islands pound', 'woocommerce' ),
381
					'GBP' => __( 'Pound sterling', 'woocommerce' ),
382
					'GEL' => __( 'Georgian lari', 'woocommerce' ),
383
					'GGP' => __( 'Guernsey pound', 'woocommerce' ),
384
					'GHS' => __( 'Ghana cedi', 'woocommerce' ),
385
					'GIP' => __( 'Gibraltar pound', 'woocommerce' ),
386
					'GMD' => __( 'Gambian dalasi', 'woocommerce' ),
387
					'GNF' => __( 'Guinean franc', 'woocommerce' ),
388
					'GTQ' => __( 'Guatemalan quetzal', 'woocommerce' ),
389
					'GYD' => __( 'Guyanese dollar', 'woocommerce' ),
390
					'HKD' => __( 'Hong Kong dollar', 'woocommerce' ),
391
					'HNL' => __( 'Honduran lempira', 'woocommerce' ),
392
					'HRK' => __( 'Croatian kuna', 'woocommerce' ),
393
					'HTG' => __( 'Haitian gourde', 'woocommerce' ),
394
					'HUF' => __( 'Hungarian forint', 'woocommerce' ),
395
					'IDR' => __( 'Indonesian rupiah', 'woocommerce' ),
396
					'ILS' => __( 'Israeli new shekel', 'woocommerce' ),
397
					'IMP' => __( 'Manx pound', 'woocommerce' ),
398
					'INR' => __( 'Indian rupee', 'woocommerce' ),
399
					'IQD' => __( 'Iraqi dinar', 'woocommerce' ),
400
					'IRR' => __( 'Iranian rial', 'woocommerce' ),
401
					'IRT' => __( 'Iranian toman', 'woocommerce' ),
402
					'ISK' => __( 'Icelandic kr&oacute;na', 'woocommerce' ),
403
					'JEP' => __( 'Jersey pound', 'woocommerce' ),
404
					'JMD' => __( 'Jamaican dollar', 'woocommerce' ),
405
					'JOD' => __( 'Jordanian dinar', 'woocommerce' ),
406
					'JPY' => __( 'Japanese yen', 'woocommerce' ),
407
					'KES' => __( 'Kenyan shilling', 'woocommerce' ),
408
					'KGS' => __( 'Kyrgyzstani som', 'woocommerce' ),
409
					'KHR' => __( 'Cambodian riel', 'woocommerce' ),
410
					'KMF' => __( 'Comorian franc', 'woocommerce' ),
411
					'KPW' => __( 'North Korean won', 'woocommerce' ),
412
					'KRW' => __( 'South Korean won', 'woocommerce' ),
413
					'KWD' => __( 'Kuwaiti dinar', 'woocommerce' ),
414
					'KYD' => __( 'Cayman Islands dollar', 'woocommerce' ),
415
					'KZT' => __( 'Kazakhstani tenge', 'woocommerce' ),
416
					'LAK' => __( 'Lao kip', 'woocommerce' ),
417
					'LBP' => __( 'Lebanese pound', 'woocommerce' ),
418
					'LKR' => __( 'Sri Lankan rupee', 'woocommerce' ),
419
					'LRD' => __( 'Liberian dollar', 'woocommerce' ),
420
					'LSL' => __( 'Lesotho loti', 'woocommerce' ),
421
					'LYD' => __( 'Libyan dinar', 'woocommerce' ),
422
					'MAD' => __( 'Moroccan dirham', 'woocommerce' ),
423
					'MDL' => __( 'Moldovan leu', 'woocommerce' ),
424
					'MGA' => __( 'Malagasy ariary', 'woocommerce' ),
425
					'MKD' => __( 'Macedonian denar', 'woocommerce' ),
426
					'MMK' => __( 'Burmese kyat', 'woocommerce' ),
427
					'MNT' => __( 'Mongolian t&ouml;gr&ouml;g', 'woocommerce' ),
428
					'MOP' => __( 'Macanese pataca', 'woocommerce' ),
429
					'MRO' => __( 'Mauritanian ouguiya', 'woocommerce' ),
430
					'MUR' => __( 'Mauritian rupee', 'woocommerce' ),
431
					'MVR' => __( 'Maldivian rufiyaa', 'woocommerce' ),
432
					'MWK' => __( 'Malawian kwacha', 'woocommerce' ),
433
					'MXN' => __( 'Mexican peso', 'woocommerce' ),
434
					'MYR' => __( 'Malaysian ringgit', 'woocommerce' ),
435
					'MZN' => __( 'Mozambican metical', 'woocommerce' ),
436
					'NAD' => __( 'Namibian dollar', 'woocommerce' ),
437
					'NGN' => __( 'Nigerian naira', 'woocommerce' ),
438
					'NIO' => __( 'Nicaraguan c&oacute;rdoba', 'woocommerce' ),
439
					'NOK' => __( 'Norwegian krone', 'woocommerce' ),
440
					'NPR' => __( 'Nepalese rupee', 'woocommerce' ),
441
					'NZD' => __( 'New Zealand dollar', 'woocommerce' ),
442
					'OMR' => __( 'Omani rial', 'woocommerce' ),
443
					'PAB' => __( 'Panamanian balboa', 'woocommerce' ),
444
					'PEN' => __( 'Sol', 'woocommerce' ),
445
					'PGK' => __( 'Papua New Guinean kina', 'woocommerce' ),
446
					'PHP' => __( 'Philippine peso', 'woocommerce' ),
447
					'PKR' => __( 'Pakistani rupee', 'woocommerce' ),
448
					'PLN' => __( 'Polish z&#x142;oty', 'woocommerce' ),
449
					'PRB' => __( 'Transnistrian ruble', 'woocommerce' ),
450
					'PYG' => __( 'Paraguayan guaran&iacute;', 'woocommerce' ),
451
					'QAR' => __( 'Qatari riyal', 'woocommerce' ),
452
					'RON' => __( 'Romanian leu', 'woocommerce' ),
453
					'RSD' => __( 'Serbian dinar', 'woocommerce' ),
454
					'RUB' => __( 'Russian ruble', 'woocommerce' ),
455
					'RWF' => __( 'Rwandan franc', 'woocommerce' ),
456
					'SAR' => __( 'Saudi riyal', 'woocommerce' ),
457
					'SBD' => __( 'Solomon Islands dollar', 'woocommerce' ),
458
					'SCR' => __( 'Seychellois rupee', 'woocommerce' ),
459
					'SDG' => __( 'Sudanese pound', 'woocommerce' ),
460
					'SEK' => __( 'Swedish krona', 'woocommerce' ),
461
					'SGD' => __( 'Singapore dollar', 'woocommerce' ),
462
					'SHP' => __( 'Saint Helena pound', 'woocommerce' ),
463
					'SLL' => __( 'Sierra Leonean leone', 'woocommerce' ),
464
					'SOS' => __( 'Somali shilling', 'woocommerce' ),
465
					'SRD' => __( 'Surinamese dollar', 'woocommerce' ),
466
					'SSP' => __( 'South Sudanese pound', 'woocommerce' ),
467
					'STD' => __( 'S&atilde;o Tom&eacute; and Pr&iacute;ncipe dobra', 'woocommerce' ),
468
					'SYP' => __( 'Syrian pound', 'woocommerce' ),
469
					'SZL' => __( 'Swazi lilangeni', 'woocommerce' ),
470
					'THB' => __( 'Thai baht', 'woocommerce' ),
471
					'TJS' => __( 'Tajikistani somoni', 'woocommerce' ),
472
					'TMT' => __( 'Turkmenistan manat', 'woocommerce' ),
473
					'TND' => __( 'Tunisian dinar', 'woocommerce' ),
474
					'TOP' => __( 'Tongan pa&#x2bb;anga', 'woocommerce' ),
475
					'TRY' => __( 'Turkish lira', 'woocommerce' ),
476
					'TTD' => __( 'Trinidad and Tobago dollar', 'woocommerce' ),
477
					'TWD' => __( 'New Taiwan dollar', 'woocommerce' ),
478
					'TZS' => __( 'Tanzanian shilling', 'woocommerce' ),
479
					'UAH' => __( 'Ukrainian hryvnia', 'woocommerce' ),
480
					'UGX' => __( 'Ugandan shilling', 'woocommerce' ),
481
					'USD' => __( 'United States (US) dollar', 'woocommerce' ),
482
					'UYU' => __( 'Uruguayan peso', 'woocommerce' ),
483
					'UZS' => __( 'Uzbekistani som', 'woocommerce' ),
484
					'VEF' => __( 'Venezuelan bol&iacute;var', 'woocommerce' ),
485
					'VES' => __( 'Bol&iacute;var soberano', 'woocommerce' ),
486
					'VND' => __( 'Vietnamese &#x111;&#x1ed3;ng', 'woocommerce' ),
487
					'VUV' => __( 'Vanuatu vatu', 'woocommerce' ),
488
					'WST' => __( 'Samoan t&#x101;l&#x101;', 'woocommerce' ),
489
					'XAF' => __( 'Central African CFA franc', 'woocommerce' ),
490
					'XCD' => __( 'East Caribbean dollar', 'woocommerce' ),
491
					'XOF' => __( 'West African CFA franc', 'woocommerce' ),
492
					'XPF' => __( 'CFP franc', 'woocommerce' ),
493
					'YER' => __( 'Yemeni rial', 'woocommerce' ),
494
					'ZAR' => __( 'South African rand', 'woocommerce' ),
495
					'ZMW' => __( 'Zambian kwacha', 'woocommerce' ),
496
				)
497
			)
498
		);
499
	}
500
501 560
	return $currencies;
502
}
503
504
505
/**
506
 * Get Currency symbol.
507
 *
508
 * @param string $currency Currency. (default: '').
509
 * @return string
510
 */
511
function get_woocommerce_currency_symbol( $currency = '' ) {
512 71
	if ( ! $currency ) {
513 56
		$currency = get_woocommerce_currency();
514
	}
515
516 71
	$symbols         = apply_filters(
517 71
		'woocommerce_currency_symbols',
518
		array(
519 71
			'AED' => '&#x62f;.&#x625;',
520
			'AFN' => '&#x60b;',
521
			'ALL' => 'L',
522
			'AMD' => 'AMD',
523
			'ANG' => '&fnof;',
524
			'AOA' => 'Kz',
525
			'ARS' => '&#36;',
526
			'AUD' => '&#36;',
527
			'AWG' => 'Afl.',
528
			'AZN' => 'AZN',
529
			'BAM' => 'KM',
530
			'BBD' => '&#36;',
531
			'BDT' => '&#2547;&nbsp;',
532
			'BGN' => '&#1083;&#1074;.',
533
			'BHD' => '.&#x62f;.&#x628;',
534
			'BIF' => 'Fr',
535
			'BMD' => '&#36;',
536
			'BND' => '&#36;',
537
			'BOB' => 'Bs.',
538
			'BRL' => '&#82;&#36;',
539
			'BSD' => '&#36;',
540
			'BTC' => '&#3647;',
541
			'BTN' => 'Nu.',
542
			'BWP' => 'P',
543
			'BYR' => 'Br',
544
			'BYN' => 'Br',
545
			'BZD' => '&#36;',
546
			'CAD' => '&#36;',
547
			'CDF' => 'Fr',
548
			'CHF' => '&#67;&#72;&#70;',
549
			'CLP' => '&#36;',
550
			'CNY' => '&yen;',
551
			'COP' => '&#36;',
552
			'CRC' => '&#x20a1;',
553
			'CUC' => '&#36;',
554
			'CUP' => '&#36;',
555
			'CVE' => '&#36;',
556
			'CZK' => '&#75;&#269;',
557
			'DJF' => 'Fr',
558
			'DKK' => 'DKK',
559
			'DOP' => 'RD&#36;',
560
			'DZD' => '&#x62f;.&#x62c;',
561
			'EGP' => 'EGP',
562
			'ERN' => 'Nfk',
563
			'ETB' => 'Br',
564
			'EUR' => '&euro;',
565
			'FJD' => '&#36;',
566
			'FKP' => '&pound;',
567
			'GBP' => '&pound;',
568
			'GEL' => '&#x20be;',
569
			'GGP' => '&pound;',
570
			'GHS' => '&#x20b5;',
571
			'GIP' => '&pound;',
572
			'GMD' => 'D',
573
			'GNF' => 'Fr',
574
			'GTQ' => 'Q',
575
			'GYD' => '&#36;',
576
			'HKD' => '&#36;',
577
			'HNL' => 'L',
578
			'HRK' => 'kn',
579
			'HTG' => 'G',
580
			'HUF' => '&#70;&#116;',
581
			'IDR' => 'Rp',
582
			'ILS' => '&#8362;',
583
			'IMP' => '&pound;',
584
			'INR' => '&#8377;',
585
			'IQD' => '&#x639;.&#x62f;',
586
			'IRR' => '&#xfdfc;',
587
			'IRT' => '&#x062A;&#x0648;&#x0645;&#x0627;&#x0646;',
588
			'ISK' => 'kr.',
589
			'JEP' => '&pound;',
590
			'JMD' => '&#36;',
591
			'JOD' => '&#x62f;.&#x627;',
592
			'JPY' => '&yen;',
593
			'KES' => 'KSh',
594
			'KGS' => '&#x441;&#x43e;&#x43c;',
595
			'KHR' => '&#x17db;',
596
			'KMF' => 'Fr',
597
			'KPW' => '&#x20a9;',
598
			'KRW' => '&#8361;',
599
			'KWD' => '&#x62f;.&#x643;',
600
			'KYD' => '&#36;',
601
			'KZT' => 'KZT',
602
			'LAK' => '&#8365;',
603
			'LBP' => '&#x644;.&#x644;',
604
			'LKR' => '&#xdbb;&#xdd4;',
605
			'LRD' => '&#36;',
606
			'LSL' => 'L',
607
			'LYD' => '&#x644;.&#x62f;',
608
			'MAD' => '&#x62f;.&#x645;.',
609
			'MDL' => 'MDL',
610
			'MGA' => 'Ar',
611
			'MKD' => '&#x434;&#x435;&#x43d;',
612
			'MMK' => 'Ks',
613
			'MNT' => '&#x20ae;',
614
			'MOP' => 'P',
615
			'MRO' => 'UM',
616
			'MUR' => '&#x20a8;',
617
			'MVR' => '.&#x783;',
618
			'MWK' => 'MK',
619
			'MXN' => '&#36;',
620
			'MYR' => '&#82;&#77;',
621
			'MZN' => 'MT',
622
			'NAD' => '&#36;',
623
			'NGN' => '&#8358;',
624
			'NIO' => 'C&#36;',
625
			'NOK' => '&#107;&#114;',
626
			'NPR' => '&#8360;',
627
			'NZD' => '&#36;',
628
			'OMR' => '&#x631;.&#x639;.',
629
			'PAB' => 'B/.',
630
			'PEN' => 'S/',
631
			'PGK' => 'K',
632
			'PHP' => '&#8369;',
633
			'PKR' => '&#8360;',
634
			'PLN' => '&#122;&#322;',
635
			'PRB' => '&#x440;.',
636
			'PYG' => '&#8370;',
637
			'QAR' => '&#x631;.&#x642;',
638
			'RMB' => '&yen;',
639
			'RON' => 'lei',
640
			'RSD' => '&#x434;&#x438;&#x43d;.',
641
			'RUB' => '&#8381;',
642
			'RWF' => 'Fr',
643
			'SAR' => '&#x631;.&#x633;',
644
			'SBD' => '&#36;',
645
			'SCR' => '&#x20a8;',
646
			'SDG' => '&#x62c;.&#x633;.',
647
			'SEK' => '&#107;&#114;',
648
			'SGD' => '&#36;',
649
			'SHP' => '&pound;',
650
			'SLL' => 'Le',
651
			'SOS' => 'Sh',
652
			'SRD' => '&#36;',
653
			'SSP' => '&pound;',
654
			'STD' => 'Db',
655
			'SYP' => '&#x644;.&#x633;',
656
			'SZL' => 'L',
657
			'THB' => '&#3647;',
658
			'TJS' => '&#x405;&#x41c;',
659
			'TMT' => 'm',
660
			'TND' => '&#x62f;.&#x62a;',
661
			'TOP' => 'T&#36;',
662
			'TRY' => '&#8378;',
663
			'TTD' => '&#36;',
664
			'TWD' => '&#78;&#84;&#36;',
665
			'TZS' => 'Sh',
666
			'UAH' => '&#8372;',
667
			'UGX' => 'UGX',
668
			'USD' => '&#36;',
669
			'UYU' => '&#36;',
670
			'UZS' => 'UZS',
671
			'VEF' => 'Bs F',
672
			'VES' => 'Bs.S',
673
			'VND' => '&#8363;',
674
			'VUV' => 'Vt',
675
			'WST' => 'T',
676
			'XAF' => 'CFA',
677
			'XCD' => '&#36;',
678
			'XOF' => 'CFA',
679
			'XPF' => 'Fr',
680
			'YER' => '&#xfdfc;',
681
			'ZAR' => '&#82;',
682
			'ZMW' => 'ZK',
683
		)
684
	);
685 71
	$currency_symbol = isset( $symbols[ $currency ] ) ? $symbols[ $currency ] : '';
686
687 71
	return apply_filters( 'woocommerce_currency_symbol', $currency_symbol, $currency );
688
}
689
690
/**
691
 * Send HTML emails from WooCommerce.
692
 *
693
 * @param mixed  $to          Receiver.
694
 * @param mixed  $subject     Subject.
695
 * @param mixed  $message     Message.
696
 * @param string $headers     Headers. (default: "Content-Type: text/html\r\n").
697
 * @param string $attachments Attachments. (default: "").
698
 */
699
function wc_mail( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = '' ) {
700
	$mailer = WC()->mailer();
701
702
	$mailer->send( $to, $subject, $message, $headers, $attachments );
703
}
704
705
/**
706
 * Return "theme support" values from the current theme, if set.
707
 *
708
 * @since  3.3.0
709
 * @param  string $prop Name of prop (or key::subkey for arrays of props) if you want a specific value. Leave blank to get all props as an array.
710
 * @param  mixed  $default Optional value to return if the theme does not declare support for a prop.
711
 * @return mixed  Value of prop(s).
712
 */
713
function wc_get_theme_support( $prop = '', $default = null ) {
714 23
	$theme_support = get_theme_support( 'woocommerce' );
715 23
	$theme_support = is_array( $theme_support ) ? $theme_support[0] : false;
716
717 23
	if ( ! $theme_support ) {
718 19
		return $default;
719
	}
720
721 5
	if ( $prop ) {
722 5
		$prop_stack = explode( '::', $prop );
723 5
		$prop_key   = array_shift( $prop_stack );
724
725 5
		if ( isset( $theme_support[ $prop_key ] ) ) {
726 5
			$value = $theme_support[ $prop_key ];
727
728 5
			if ( count( $prop_stack ) ) {
729 5
				foreach ( $prop_stack as $prop_key ) {
730
					if ( is_array( $value ) && isset( $value[ $prop_key ] ) ) {
731
						$value = $value[ $prop_key ];
732
					} else {
733
						$value = $default;
734
						break;
735
					}
736
				}
737
			}
738
		} else {
739 1
			$value = $default;
740
		}
741
742 5
		return $value;
743
	}
744
745 1
	return $theme_support;
746
}
747
748
/**
749
 * Get an image size by name or defined dimensions.
750
 *
751
 * The returned variable is filtered by woocommerce_get_image_size_{image_size} filter to
752
 * allow 3rd party customisation.
753
 *
754
 * Sizes defined by the theme take priority over settings. Settings are hidden when a theme
755
 * defines sizes.
756
 *
757
 * @param array|string $image_size Name of the image size to get, or an array of dimensions.
758
 * @return array Array of dimensions including width, height, and cropping mode. Cropping mode is 0 for no crop, and 1 for hard crop.
759
 */
760
function wc_get_image_size( $image_size ) {
761 16
	$cache_key = 'size-' . ( is_array( $image_size ) ? implode( '-', $image_size ) : $image_size );
762 16
	$size      = wp_cache_get( $cache_key, 'woocommerce' );
763
764 16
	if ( $size ) {
765 4
		return $size;
766
	}
767
768
	$size = array(
769 16
		'width'  => 600,
770
		'height' => 600,
771
		'crop'   => 1,
772
	);
773
774 16
	if ( is_array( $image_size ) ) {
775
		$size       = array(
776 1
			'width'  => isset( $image_size[0] ) ? absint( $image_size[0] ) : 600,
777 1
			'height' => isset( $image_size[1] ) ? absint( $image_size[1] ) : 600,
778 1
			'crop'   => isset( $image_size[2] ) ? absint( $image_size[2] ) : 1,
779
		);
780 1
		$image_size = $size['width'] . '_' . $size['height'];
781
	} else {
782 16
		$image_size = str_replace( 'woocommerce_', '', $image_size );
783
784
		// Legacy size mapping.
785 16
		if ( 'shop_single' === $image_size ) {
786 1
			$image_size = 'single';
787 16
		} elseif ( 'shop_catalog' === $image_size ) {
788
			$image_size = 'thumbnail';
789 16
		} elseif ( 'shop_thumbnail' === $image_size ) {
790 1
			$image_size = 'gallery_thumbnail';
791
		}
792
793 16
		if ( 'single' === $image_size ) {
794 1
			$size['width']  = absint( wc_get_theme_support( 'single_image_width', get_option( 'woocommerce_single_image_width', 600 ) ) );
795 1
			$size['height'] = '';
796 1
			$size['crop']   = 0;
797
798 16
		} elseif ( 'gallery_thumbnail' === $image_size ) {
799 1
			$size['width']  = absint( wc_get_theme_support( 'gallery_thumbnail_image_width', 100 ) );
800 1
			$size['height'] = $size['width'];
801 1
			$size['crop']   = 1;
802
803 15
		} elseif ( 'thumbnail' === $image_size ) {
804 15
			$size['width'] = absint( wc_get_theme_support( 'thumbnail_image_width', get_option( 'woocommerce_thumbnail_image_width', 300 ) ) );
805 15
			$cropping      = get_option( 'woocommerce_thumbnail_cropping', '1:1' );
806
807 15
			if ( 'uncropped' === $cropping ) {
808
				$size['height'] = '';
809
				$size['crop']   = 0;
810 15
			} elseif ( 'custom' === $cropping ) {
811
				$width          = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
812
				$height         = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) );
813
				$size['height'] = absint( round( ( $size['width'] / $width ) * $height ) );
814
				$size['crop']   = 1;
815
			} else {
816 15
				$cropping_split = explode( ':', $cropping );
817 15
				$width          = max( 1, current( $cropping_split ) );
818 15
				$height         = max( 1, end( $cropping_split ) );
819 15
				$size['height'] = absint( round( ( $size['width'] / $width ) * $height ) );
820 15
				$size['crop']   = 1;
821
			}
822
		}
823
	}
824
825 16
	$size = apply_filters( 'woocommerce_get_image_size_' . $image_size, $size );
826
827 16
	wp_cache_set( $cache_key, $size, 'woocommerce' );
828
829 16
	return $size;
830
}
831
832
/**
833
 * Queue some JavaScript code to be output in the footer.
834
 *
835
 * @param string $code Code.
836
 */
837
function wc_enqueue_js( $code ) {
838
	global $wc_queued_js;
839
840 1
	if ( empty( $wc_queued_js ) ) {
841 1
		$wc_queued_js = '';
842
	}
843
844 1
	$wc_queued_js .= "\n" . $code . "\n";
845
}
846
847
/**
848
 * Output any queued javascript code in the footer.
849
 */
850
function wc_print_js() {
851
	global $wc_queued_js;
852
853 1
	if ( ! empty( $wc_queued_js ) ) {
854
		// Sanitize.
855 1
		$wc_queued_js = wp_check_invalid_utf8( $wc_queued_js );
856 1
		$wc_queued_js = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", $wc_queued_js );
857 1
		$wc_queued_js = str_replace( "\r", '', $wc_queued_js );
858
859 1
		$js = "<!-- WooCommerce JavaScript -->\n<script type=\"text/javascript\">\njQuery(function($) { $wc_queued_js });\n</script>\n";
860
861
		/**
862
		 * Queued jsfilter.
863
		 *
864
		 * @since 2.6.0
865
		 * @param string $js JavaScript code.
866
		 */
867 1
		echo apply_filters( 'woocommerce_queued_js', $js ); // WPCS: XSS ok.
868
869 1
		unset( $wc_queued_js );
870
	}
871
}
872
873
/**
874
 * Set a cookie - wrapper for setcookie using WP constants.
875
 *
876
 * @param  string  $name   Name of the cookie being set.
877
 * @param  string  $value  Value of the cookie.
878
 * @param  integer $expire Expiry of the cookie.
879
 * @param  bool    $secure Whether the cookie should be served only over https.
880
 * @param  bool    $httponly Whether the cookie is only accessible over HTTP, not scripting languages like JavaScript. @since 3.6.0.
881
 */
882
function wc_setcookie( $name, $value, $expire = 0, $secure = false, $httponly = false ) {
883
	if ( ! headers_sent() ) {
884
		setcookie( $name, $value, $expire, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, $secure, apply_filters( 'woocommerce_cookie_httponly', $httponly, $name, $value, $expire, $secure ) );
0 ignored issues
show
introduced by
Due to using Batcache, server side based client related logic will not work, use JS instead.
Loading history...
885 View Code Duplication
	} elseif ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
886
		headers_sent( $file, $line );
887
		trigger_error( "{$name} cookie cannot be set - headers already sent by {$file} on line {$line}", E_USER_NOTICE ); // @codingStandardsIgnoreLine
888
	}
889
}
890
891
/**
892
 * Get the URL to the WooCommerce REST API.
893
 *
894
 * @since 2.1
895
 * @param string $path an endpoint to include in the URL.
896
 * @return string the URL.
897
 */
898
function get_woocommerce_api_url( $path ) {
899 1
	$version = defined( 'WC_API_REQUEST_VERSION' ) ? WC_API_REQUEST_VERSION : substr( WC_API::VERSION, 0, 1 );
900
901 1
	$url = get_home_url( null, "wc-api/v{$version}/", is_ssl() ? 'https' : 'http' );
902
903 1
	if ( ! empty( $path ) && is_string( $path ) ) {
904 1
		$url .= ltrim( $path, '/' );
905
	}
906
907 1
	return $url;
908
}
909
910
/**
911
 * Get a log file path.
912
 *
913
 * @since 2.2
914
 *
915
 * @param string $handle name.
916
 * @return string the log file path.
917
 */
918
function wc_get_log_file_path( $handle ) {
919 2
	return WC_Log_Handler_File::get_log_file_path( $handle );
920
}
921
922
/**
923
 * Get a log file name.
924
 *
925
 * @since 3.3
926
 *
927
 * @param string $handle Name.
928
 * @return string The log file name.
929
 */
930
function wc_get_log_file_name( $handle ) {
931 2
	return WC_Log_Handler_File::get_log_file_name( $handle );
932
}
933
934
/**
935
 * Recursively get page children.
936
 *
937
 * @param  int $page_id Page ID.
938
 * @return int[]
939
 */
940
function wc_get_page_children( $page_id ) {
941 1
	$page_ids = get_posts(
942
		array(
943 1
			'post_parent' => $page_id,
944 1
			'post_type'   => 'page',
945
			'numberposts' => -1, // @codingStandardsIgnoreLine
946 1
			'post_status' => 'any',
947 1
			'fields'      => 'ids',
948
		)
949
	);
950
951 1
	if ( ! empty( $page_ids ) ) {
952 1
		foreach ( $page_ids as $page_id ) {
953 1
			$page_ids = array_merge( $page_ids, wc_get_page_children( $page_id ) );
954
		}
955
	}
956
957 1
	return $page_ids;
958
}
959
960
/**
961
 * Flushes rewrite rules when the shop page (or it's children) gets saved.
962
 */
963
function flush_rewrite_rules_on_shop_page_save() {
964
	$screen    = get_current_screen();
965
	$screen_id = $screen ? $screen->id : '';
966
967
	// Check if this is the edit page.
968
	if ( 'page' !== $screen_id ) {
969
		return;
970
	}
971
972
	// Check if page is edited.
973
	if ( empty( $_GET['post'] ) || empty( $_GET['action'] ) || ( isset( $_GET['action'] ) && 'edit' !== $_GET['action'] ) ) { // WPCS: input var ok, CSRF ok.
0 ignored issues
show
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
974
		return;
975
	}
976
977
	$post_id      = intval( $_GET['post'] ); // WPCS: input var ok, CSRF ok.
978
	$shop_page_id = wc_get_page_id( 'shop' );
979
980
	if ( $shop_page_id === $post_id || in_array( $post_id, wc_get_page_children( $shop_page_id ), true ) ) {
981
		do_action( 'woocommerce_flush_rewrite_rules' );
982
	}
983
}
984
add_action( 'admin_footer', 'flush_rewrite_rules_on_shop_page_save' );
985
986
/**
987
 * Various rewrite rule fixes.
988
 *
989
 * @since 2.2
990
 * @param array $rules Rules.
991
 * @return array
992
 */
993
function wc_fix_rewrite_rules( $rules ) {
994
	global $wp_rewrite;
995
996
	$permalinks = wc_get_permalink_structure();
997
998
	// Fix the rewrite rules when the product permalink have %product_cat% flag.
999
	if ( preg_match( '`/(.+)(/%product_cat%)`', $permalinks['product_rewrite_slug'], $matches ) ) {
1000
		foreach ( $rules as $rule => $rewrite ) {
1001
			if ( preg_match( '`^' . preg_quote( $matches[1], '`' ) . '/\(`', $rule ) && preg_match( '/^(index\.php\?product_cat)(?!(.*product))/', $rewrite ) ) {
1002
				unset( $rules[ $rule ] );
1003
			}
1004
		}
1005
	}
1006
1007
	// If the shop page is used as the base, we need to handle shop page subpages to avoid 404s.
1008
	if ( ! $permalinks['use_verbose_page_rules'] ) {
1009
		return $rules;
1010
	}
1011
1012
	$shop_page_id = wc_get_page_id( 'shop' );
1013
	if ( $shop_page_id ) {
1014
		$page_rewrite_rules = array();
1015
		$subpages           = wc_get_page_children( $shop_page_id );
1016
1017
		// Subpage rules.
1018
		foreach ( $subpages as $subpage ) {
1019
			$uri                                = get_page_uri( $subpage );
1020
			$page_rewrite_rules[ $uri . '/?$' ] = 'index.php?pagename=' . $uri;
1021
			$wp_generated_rewrite_rules         = $wp_rewrite->generate_rewrite_rules( $uri, EP_PAGES, true, true, false, false );
1022
			foreach ( $wp_generated_rewrite_rules as $key => $value ) {
1023
				$wp_generated_rewrite_rules[ $key ] = $value . '&pagename=' . $uri;
1024
			}
1025
			$page_rewrite_rules = array_merge( $page_rewrite_rules, $wp_generated_rewrite_rules );
1026
		}
1027
1028
		// Merge with rules.
1029
		$rules = array_merge( $page_rewrite_rules, $rules );
1030
	}
1031
1032
	return $rules;
1033
}
1034
add_filter( 'rewrite_rules_array', 'wc_fix_rewrite_rules' );
1035
1036
/**
1037
 * Prevent product attachment links from breaking when using complex rewrite structures.
1038
 *
1039
 * @param  string $link    Link.
1040
 * @param  int    $post_id Post ID.
1041
 * @return string
1042
 */
1043
function wc_fix_product_attachment_link( $link, $post_id ) {
1044 1
	$parent_type = get_post_type( wp_get_post_parent_id( $post_id ) );
1045 1
	if ( 'product' === $parent_type || 'product_variation' === $parent_type ) {
1046
		$link = home_url( '/?attachment_id=' . $post_id );
1047
	}
1048 1
	return $link;
1049
}
1050
add_filter( 'attachment_link', 'wc_fix_product_attachment_link', 10, 2 );
1051
1052
/**
1053
 * Protect downloads from ms-files.php in multisite.
1054
 *
1055
 * @param string $rewrite rewrite rules.
1056
 * @return string
1057
 */
1058
function wc_ms_protect_download_rewite_rules( $rewrite ) {
1059
	if ( ! is_multisite() || 'redirect' === get_option( 'woocommerce_file_download_method' ) ) {
1060
		return $rewrite;
1061
	}
1062
1063
	$rule  = "\n# WooCommerce Rules - Protect Files from ms-files.php\n\n";
1064
	$rule .= "<IfModule mod_rewrite.c>\n";
1065
	$rule .= "RewriteEngine On\n";
1066
	$rule .= "RewriteCond %{QUERY_STRING} file=woocommerce_uploads/ [NC]\n";
1067
	$rule .= "RewriteRule /ms-files.php$ - [F]\n";
1068
	$rule .= "</IfModule>\n\n";
1069
1070
	return $rule . $rewrite;
1071
}
1072
add_filter( 'mod_rewrite_rules', 'wc_ms_protect_download_rewite_rules' );
1073
1074
/**
1075
 * Formats a string in the format COUNTRY:STATE into an array.
1076
 *
1077
 * @since 2.3.0
1078
 * @param  string $country_string Country string.
1079
 * @return array
1080
 */
1081
function wc_format_country_state_string( $country_string ) {
1082 49
	if ( strstr( $country_string, ':' ) ) {
1083 1
		list( $country, $state ) = explode( ':', $country_string );
1084
	} else {
1085 49
		$country = $country_string;
1086 49
		$state   = '';
1087
	}
1088
	return array(
1089 49
		'country' => $country,
1090 49
		'state'   => $state,
1091
	);
1092
}
1093
1094
/**
1095
 * Get the store's base location.
1096
 *
1097
 * @since 2.3.0
1098
 * @return array
1099
 */
1100
function wc_get_base_location() {
1101 43
	$default = apply_filters( 'woocommerce_get_base_location', get_option( 'woocommerce_default_country' ) );
1102
1103 43
	return wc_format_country_state_string( $default );
1104
}
1105
1106
/**
1107
 * Get the customer's default location.
1108
 *
1109
 * Filtered, and set to base location or left blank. If cache-busting,
1110
 * this should only be used when 'location' is set in the querystring.
1111
 *
1112
 * @since 2.3.0
1113
 * @return array
1114
 */
1115
function wc_get_customer_default_location() {
1116 8
	$set_default_location_to = get_option( 'woocommerce_default_customer_address', 'geolocation' );
1117 8
	$default_location        = '' === $set_default_location_to ? '' : get_option( 'woocommerce_default_country', '' );
1118 8
	$location                = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', $default_location ) );
1119
1120
	// Geolocation takes priority if used and if geolocation is possible.
1121 8
	if ( 'geolocation' === $set_default_location_to || 'geolocation_ajax' === $set_default_location_to ) {
1122 8
		$ua = wc_get_user_agent();
1123
1124
		// Exclude common bots from geolocation by user agent.
1125 8
		if ( ! stristr( $ua, 'bot' ) && ! stristr( $ua, 'spider' ) && ! stristr( $ua, 'crawl' ) ) {
1126 8
			$geolocation = WC_Geolocation::geolocate_ip( '', true, false );
1127
1128 8
			if ( ! empty( $geolocation['country'] ) ) {
1129 1
				$location = $geolocation;
1130
			}
1131
		}
1132
	}
1133
1134
	// Once we have a location, ensure it's valid, otherwise fallback to a valid location.
1135 8
	$allowed_country_codes = WC()->countries->get_allowed_countries();
1136
1137 8
	if ( ! empty( $location['country'] ) && ! array_key_exists( $location['country'], $allowed_country_codes ) ) {
1138
		$location['country'] = current( $allowed_country_codes );
1139
		$location['state']   = '';
1140
	}
1141
1142 8
	return apply_filters( 'woocommerce_customer_default_location_array', $location );
1143
}
1144
1145
/**
1146
 * Get user agent string.
1147
 *
1148
 * @since  3.0.0
1149
 * @return string
1150
 */
1151
function wc_get_user_agent() {
1152 105
	return isset( $_SERVER['HTTP_USER_AGENT'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; // @codingStandardsIgnoreLine
1153
}
1154
1155
// This function can be removed when WP 3.9.2 or greater is required.
1156
if ( ! function_exists( 'hash_equals' ) ) :
1157
	/**
1158
	 * Compare two strings in constant time.
1159
	 *
1160
	 * This function was added in PHP 5.6.
1161
	 * It can leak the length of a string.
1162
	 *
1163
	 * @since 3.9.2
1164
	 *
1165
	 * @param string $a Expected string.
1166
	 * @param string $b Actual string.
1167
	 * @return bool Whether strings are equal.
1168
	 */
1169
	function hash_equals( $a, $b ) {
1170
		$a_length = strlen( $a );
1171
		if ( strlen( $b ) !== $a_length ) {
1172
			return false;
1173
		}
1174
		$result = 0;
1175
1176
		// Do not attempt to "optimize" this.
1177
		for ( $i = 0; $i < $a_length; $i++ ) {
1178
			$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
1179
		}
1180
1181
		return 0 === $result;
1182
	}
1183
endif;
1184
1185
/**
1186
 * Generate a rand hash.
1187
 *
1188
 * @since  2.4.0
1189
 * @return string
1190
 */
1191
function wc_rand_hash() {
1192 1
	if ( ! function_exists( 'openssl_random_pseudo_bytes' ) ) {
1193
		return sha1( wp_rand() );
1194
	}
1195
1196 1
	return bin2hex( openssl_random_pseudo_bytes( 20 ) ); // @codingStandardsIgnoreLine
1197
}
1198
1199
/**
1200
 * WC API - Hash.
1201
 *
1202
 * @since  2.4.0
1203
 * @param  string $data Message to be hashed.
1204
 * @return string
1205
 */
1206
function wc_api_hash( $data ) {
1207
	return hash_hmac( 'sha256', $data, 'wc-api' );
1208
}
1209
1210
/**
1211
 * Find all possible combinations of values from the input array and return in a logical order.
1212
 *
1213
 * @since 2.5.0
1214
 * @param array $input Input.
1215
 * @return array
1216
 */
1217
function wc_array_cartesian( $input ) {
1218 3
	$input   = array_filter( $input );
1219 3
	$results = array();
1220 3
	$indexes = array();
1221 3
	$index   = 0;
1222
1223
	// Generate indexes from keys and values so we have a logical sort order.
1224 3
	foreach ( $input as $key => $values ) {
1225 3
		foreach ( $values as $value ) {
1226 3
			$indexes[ $key ][ $value ] = $index++;
1227
		}
1228
	}
1229
1230
	// Loop over the 2D array of indexes and generate all combinations.
1231 3
	foreach ( $indexes as $key => $values ) {
1232
		// When result is empty, fill with the values of the first looped array.
1233 3
		if ( empty( $results ) ) {
1234 3
			foreach ( $values as $value ) {
1235 3
				$results[] = array( $key => $value );
1236
			}
1237
		} else {
1238
			// Second and subsequent input sub-array merging.
1239 3
			foreach ( $results as $result_key => $result ) {
1240 3
				foreach ( $values as $value ) {
1241
					// If the key is not set, we can set it.
1242 3
					if ( ! isset( $results[ $result_key ][ $key ] ) ) {
1243 3
						$results[ $result_key ][ $key ] = $value;
1244
					} else {
1245
						// If the key is set, we can add a new combination to the results array.
1246 3
						$new_combination         = $results[ $result_key ];
1247 3
						$new_combination[ $key ] = $value;
1248 3
						$results[]               = $new_combination;
1249
					}
1250
				}
1251
			}
1252
		}
1253
	}
1254
1255
	// Sort the indexes.
1256 3
	arsort( $results );
1257
1258
	// Convert indexes back to values.
1259 3
	foreach ( $results as $result_key => $result ) {
1260 3
		$converted_values = array();
1261
1262
		// Sort the values.
1263 3
		arsort( $results[ $result_key ] );
1264
1265
		// Convert the values.
1266 3
		foreach ( $results[ $result_key ] as $key => $value ) {
1267 3
			$converted_values[ $key ] = array_search( $value, $indexes[ $key ], true );
1268
		}
1269
1270 3
		$results[ $result_key ] = $converted_values;
1271
	}
1272
1273 3
	return $results;
1274
}
1275
1276
/**
1277
 * Run a MySQL transaction query, if supported.
1278
 *
1279
 * @since 2.5.0
1280
 * @param string $type Types: start (default), commit, rollback.
1281
 * @param bool   $force use of transactions.
1282
 */
1283
function wc_transaction_query( $type = 'start', $force = false ) {
1284
	global $wpdb;
1285
1286 1
	$wpdb->hide_errors();
1287
1288 1
	wc_maybe_define_constant( 'WC_USE_TRANSACTIONS', true );
1289
1290 1
	if ( WC_USE_TRANSACTIONS || $force ) {
1291 1
		switch ( $type ) {
1292
			case 'commit':
1293 1
				$wpdb->query( 'COMMIT' );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
1294 1
				break;
1295
			case 'rollback':
1296 1
				$wpdb->query( 'ROLLBACK' );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
1297 1
				break;
1298
			default:
1299 1
				$wpdb->query( 'START TRANSACTION' );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
1300 1
				break;
1301
		}
1302
	}
1303
}
1304
1305
/**
1306
 * Gets the url to the cart page.
1307
 *
1308
 * @since  2.5.0
1309
 *
1310
 * @return string Url to cart page
1311
 */
1312
function wc_get_cart_url() {
1313 1
	return apply_filters( 'woocommerce_get_cart_url', wc_get_page_permalink( 'cart' ) );
1314
}
1315
1316
/**
1317
 * Gets the url to the checkout page.
1318
 *
1319
 * @since  2.5.0
1320
 *
1321
 * @return string Url to checkout page
1322
 */
1323
function wc_get_checkout_url() {
1324 2
	$checkout_url = wc_get_page_permalink( 'checkout' );
1325 2 View Code Duplication
	if ( $checkout_url ) {
1326
		// Force SSL if needed.
1327 2
		if ( is_ssl() || 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) ) {
1328 1
			$checkout_url = str_replace( 'http:', 'https:', $checkout_url );
1329
		}
1330
	}
1331
1332 2
	return apply_filters( 'woocommerce_get_checkout_url', $checkout_url );
1333
}
1334
1335
/**
1336
 * Register a shipping method.
1337
 *
1338
 * @since 1.5.7
1339
 * @param string|object $shipping_method class name (string) or a class object.
1340
 */
1341
function woocommerce_register_shipping_method( $shipping_method ) {
1342
	WC()->shipping()->register_shipping_method( $shipping_method );
1343
}
1344
1345
if ( ! function_exists( 'wc_get_shipping_zone' ) ) {
1346
	/**
1347
	 * Get the shipping zone matching a given package from the cart.
1348
	 *
1349
	 * @since  2.6.0
1350
	 * @uses   WC_Shipping_Zones::get_zone_matching_package
1351
	 * @param  array $package Shipping package.
1352
	 * @return WC_Shipping_Zone
1353
	 */
1354
	function wc_get_shipping_zone( $package ) {
1355
		return WC_Shipping_Zones::get_zone_matching_package( $package );
1356
	}
1357
}
1358
1359
/**
1360
 * Get a nice name for credit card providers.
1361
 *
1362
 * @since  2.6.0
1363
 * @param  string $type Provider Slug/Type.
1364
 * @return string
1365
 */
1366
function wc_get_credit_card_type_label( $type ) {
1367
	// Normalize.
1368 1
	$type = strtolower( $type );
1369 1
	$type = str_replace( '-', ' ', $type );
1370 1
	$type = str_replace( '_', ' ', $type );
1371
1372 1
	$labels = apply_filters(
1373 1
		'woocommerce_credit_card_type_labels',
1374
		array(
1375 1
			'mastercard'       => __( 'MasterCard', 'woocommerce' ),
1376 1
			'visa'             => __( 'Visa', 'woocommerce' ),
1377 1
			'discover'         => __( 'Discover', 'woocommerce' ),
1378 1
			'american express' => __( 'American Express', 'woocommerce' ),
1379 1
			'diners'           => __( 'Diners', 'woocommerce' ),
1380 1
			'jcb'              => __( 'JCB', 'woocommerce' ),
1381
		)
1382
	);
1383
1384 1
	return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
1385
}
1386
1387
/**
1388
 * Outputs a "back" link so admin screens can easily jump back a page.
1389
 *
1390
 * @param string $label Title of the page to return to.
1391
 * @param string $url   URL of the page to return to.
1392
 */
1393
function wc_back_link( $label, $url ) {
1394
	echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" aria-label="' . esc_attr( $label ) . '">&#x2934;</a></small>';
1395
}
1396
1397
/**
1398
 * Display a WooCommerce help tip.
1399
 *
1400
 * @since  2.5.0
1401
 *
1402
 * @param  string $tip        Help tip text.
1403
 * @param  bool   $allow_html Allow sanitized HTML if true or escape.
1404
 * @return string
1405
 */
1406
function wc_help_tip( $tip, $allow_html = false ) {
1407 10
	if ( $allow_html ) {
1408 10
		$tip = wc_sanitize_tooltip( $tip );
1409
	} else {
1410
		$tip = esc_attr( $tip );
1411
	}
1412
1413 10
	return '<span class="woocommerce-help-tip" data-tip="' . $tip . '"></span>';
1414
}
1415
1416
/**
1417
 * Return a list of potential postcodes for wildcard searching.
1418
 *
1419
 * @since 2.6.0
1420
 * @param  string $postcode Postcode.
1421
 * @param  string $country  Country to format postcode for matching.
1422
 * @return string[]
1423
 */
1424
function wc_get_wildcard_postcodes( $postcode, $country = '' ) {
1425 131
	$formatted_postcode = wc_format_postcode( $postcode, $country );
1426 131
	$length             = function_exists( 'mb_strlen' ) ? mb_strlen( $formatted_postcode ) : strlen( $formatted_postcode );
1427
	$postcodes          = array(
1428 131
		$postcode,
1429 131
		$formatted_postcode,
1430 131
		$formatted_postcode . '*',
1431
	);
1432
1433 131
	for ( $i = 0; $i < $length; $i ++ ) {
1434 8
		$postcodes[] = ( function_exists( 'mb_substr' ) ? mb_substr( $formatted_postcode, 0, ( $i + 1 ) * -1 ) : substr( $formatted_postcode, 0, ( $i + 1 ) * -1 ) ) . '*';
1435
	}
1436
1437 131
	return $postcodes;
1438
}
1439
1440
/**
1441
 * Used by shipping zones and taxes to compare a given $postcode to stored
1442
 * postcodes to find matches for numerical ranges, and wildcards.
1443
 *
1444
 * @since 2.6.0
1445
 * @param string $postcode           Postcode you want to match against stored postcodes.
1446
 * @param array  $objects            Array of postcode objects from Database.
1447
 * @param string $object_id_key      DB column name for the ID.
1448
 * @param string $object_compare_key DB column name for the value.
1449
 * @param string $country            Country from which this postcode belongs. Allows for formatting.
1450
 * @return array Array of matching object ID and matching values.
1451
 */
1452
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key, $country = '' ) {
1453 1
	$postcode           = wc_normalize_postcode( $postcode );
1454 1
	$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode, $country ) );
1455 1
	$matches            = array();
1456
1457 1
	foreach ( $objects as $object ) {
1458 1
		$object_id       = $object->$object_id_key;
1459 1
		$compare_against = $object->$object_compare_key;
1460
1461
		// Handle postcodes containing ranges.
1462 1
		if ( strstr( $compare_against, '...' ) ) {
1463
			$range = array_map( 'trim', explode( '...', $compare_against ) );
1464
1465
			if ( 2 !== count( $range ) ) {
1466
				continue;
1467
			}
1468
1469
			list( $min, $max ) = $range;
1470
1471
			// If the postcode is non-numeric, make it numeric.
1472
			if ( ! is_numeric( $min ) || ! is_numeric( $max ) ) {
1473
				$compare = wc_make_numeric_postcode( $postcode );
1474
				$min     = str_pad( wc_make_numeric_postcode( $min ), strlen( $compare ), '0' );
1475
				$max     = str_pad( wc_make_numeric_postcode( $max ), strlen( $compare ), '0' );
1476
			} else {
1477
				$compare = $postcode;
1478
			}
1479
1480
			if ( $compare >= $min && $compare <= $max ) {
1481
				$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ] : array();
1482
				$matches[ $object_id ][] = $compare_against;
1483
			}
1484 1
		} elseif ( in_array( $compare_against, $wildcard_postcodes, true ) ) {
1485
			// Wildcard and standard comparison.
1486 1
			$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ] : array();
1487 1
			$matches[ $object_id ][] = $compare_against;
1488
		}
1489
	}
1490
1491 1
	return $matches;
1492
}
1493
1494
/**
1495
 * Gets number of shipping methods currently enabled. Used to identify if
1496
 * shipping is configured.
1497
 *
1498
 * @since  2.6.0
1499
 * @param  bool $include_legacy Count legacy shipping methods too.
1500
 * @return int
1501
 */
1502
function wc_get_shipping_method_count( $include_legacy = false ) {
1503
	global $wpdb;
1504
1505 83
	$transient_name    = $include_legacy ? 'wc_shipping_method_count_legacy' : 'wc_shipping_method_count';
1506 83
	$transient_version = WC_Cache_Helper::get_transient_version( 'shipping' );
1507 83
	$transient_value   = get_transient( $transient_name );
1508
1509 83
	if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) {
1510 52
		return absint( $transient_value['value'] );
1511
	}
1512
1513 83
	$method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
1514
1515 83
	if ( $include_legacy ) {
1516
		// Count activated methods that don't support shipping zones.
1517 83
		$methods = WC()->shipping()->get_shipping_methods();
1518
1519 83
		foreach ( $methods as $method ) {
0 ignored issues
show
Bug introduced by
The expression $methods of type null|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...
1520 83
			if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) {
1521 16
				$method_count++;
1522
			}
1523
		}
1524
	}
1525
1526
	$transient_value = array(
1527 83
		'version' => $transient_version,
1528 83
		'value'   => $method_count,
1529
	);
1530
1531 83
	set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 );
1532
1533 83
	return $method_count;
1534
}
1535
1536
/**
1537
 * Wrapper for set_time_limit to see if it is enabled.
1538
 *
1539
 * @since 2.6.0
1540
 * @param int $limit Time limit.
1541
 */
1542
function wc_set_time_limit( $limit = 0 ) {
1543 1
	if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
1544 1
		@set_time_limit( $limit ); // @codingStandardsIgnoreLine
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1545
	}
1546
}
1547
1548
/**
1549
 * Wrapper for nocache_headers which also disables page caching.
1550
 *
1551
 * @since 3.2.4
1552
 */
1553
function wc_nocache_headers() {
1554
	WC_Cache_Helper::set_nocache_constants();
1555
	nocache_headers();
1556
}
1557
1558
/**
1559
 * Used to sort products attributes with uasort.
1560
 *
1561
 * @since 2.6.0
1562
 * @param array $a First attribute to compare.
1563
 * @param array $b Second attribute to compare.
1564
 * @return int
1565
 */
1566
function wc_product_attribute_uasort_comparison( $a, $b ) {
1567 7
	return wc_uasort_comparison( $a['position'], $b['position'] );
1568
}
1569
1570
/**
1571
 * Used to sort shipping zone methods with uasort.
1572
 *
1573
 * @since 3.0.0
1574
 * @param array $a First shipping zone method to compare.
1575
 * @param array $b Second shipping zone method to compare.
1576
 * @return int
1577
 */
1578
function wc_shipping_zone_method_order_uasort_comparison( $a, $b ) {
1579 1
	return wc_uasort_comparison( $a->method_order, $b->method_order );
1580
}
1581
1582
/**
1583
 * User to sort checkout fields based on priority with uasort.
1584
 *
1585
 * @since 3.5.1
1586
 * @param array $a First field to compare.
1587
 * @param array $b Second field to compare.
1588
 * @return int
1589
 */
1590
function wc_checkout_fields_uasort_comparison( $a, $b ) {
1591
	/*
1592
	 * We are not guaranteed to get a priority
1593
	 * setting. So don't compare if they don't
1594
	 * exist.
1595
	 */
1596 2
	if ( ! isset( $a['priority'], $b['priority'] ) ) {
1597
		return 0;
1598
	}
1599
1600 2
	return wc_uasort_comparison( $a['priority'], $b['priority'] );
1601
}
1602
1603
/**
1604
 * User to sort two values with ausort.
1605
 *
1606
 * @since 3.5.1
1607
 * @param int $a First value to compare.
1608
 * @param int $b Second value to compare.
1609
 * @return int
1610
 */
1611
function wc_uasort_comparison( $a, $b ) {
1612 10
	if ( $a === $b ) {
1613 5
		return 0;
1614
	}
1615 5
	return ( $a < $b ) ? -1 : 1;
1616
}
1617
1618
/**
1619
 * Sort values based on ascii, usefull for special chars in strings.
1620
 *
1621
 * @param string $a First value.
1622
 * @param string $b Second value.
1623
 * @return int
1624
 */
1625
function wc_ascii_uasort_comparison( $a, $b ) {
1626 6
	if ( function_exists( 'iconv' ) && defined( 'ICONV_IMPL' ) && @strcasecmp( ICONV_IMPL, 'unknown' ) !== 0 ) {
0 ignored issues
show
introduced by
Found "!== 0". Use Yoda Condition checks, you must
Loading history...
Coding Style introduced by
Silencing errors is discouraged
Loading history...
1627 6
		$a = @iconv( 'UTF-8', 'ASCII//TRANSLIT//IGNORE', $a );
0 ignored issues
show
Coding Style introduced by
Silencing errors is discouraged
Loading history...
1628 6
		$b = @iconv( 'UTF-8', 'ASCII//TRANSLIT//IGNORE', $b );
0 ignored issues
show
Coding Style introduced by
Silencing errors is discouraged
Loading history...
1629
	}
1630 6
	return strcmp( $a, $b );
1631
}
1632
1633
/**
1634
 * Get rounding mode for internal tax calculations.
1635
 *
1636
 * @since 3.2.4
1637
 * @return int
1638
 */
1639
function wc_get_tax_rounding_mode() {
1640 124
	$constant = WC_TAX_ROUNDING_MODE;
1641
1642 124
	if ( 'auto' === $constant ) {
1643 124
		return 'yes' === get_option( 'woocommerce_prices_include_tax', 'no' ) ? 2 : 1;
1644
	}
1645
1646
	return intval( $constant );
1647
}
1648
1649
/**
1650
 * Get rounding precision for internal WC calculations.
1651
 * Will increase the precision of wc_get_price_decimals by 2 decimals, unless WC_ROUNDING_PRECISION is set to a higher number.
1652
 *
1653
 * @since 2.6.3
1654
 * @return int
1655
 */
1656
function wc_get_rounding_precision() {
1657 244
	$precision = wc_get_price_decimals() + 2;
1658 244
	if ( absint( WC_ROUNDING_PRECISION ) > $precision ) {
1659 244
		$precision = absint( WC_ROUNDING_PRECISION );
1660
	}
1661 244
	return $precision;
1662
}
1663
1664
/**
1665
 * Add precision to a number and return a number.
1666
 *
1667
 * @since  3.2.0
1668
 * @param  float $value Number to add precision to.
1669
 * @param  bool  $round If should round after adding precision.
1670
 * @return int|float
1671
 */
1672
function wc_add_number_precision( $value, $round = true ) {
1673 104
	$cent_precision = pow( 10, wc_get_price_decimals() );
1674 104
	$value          = $value * $cent_precision;
1675 104
	return $round ? round( $value, wc_get_rounding_precision() - wc_get_price_decimals() ) : $value;
1676
}
1677
1678
/**
1679
 * Remove precision from a number and return a float.
1680
 *
1681
 * @since  3.2.0
1682
 * @param  float $value Number to add precision to.
1683
 * @return float
1684
 */
1685
function wc_remove_number_precision( $value ) {
1686 104
	$cent_precision = pow( 10, wc_get_price_decimals() );
1687 104
	return $value / $cent_precision;
1688
}
1689
1690
/**
1691
 * Add precision to an array of number and return an array of int.
1692
 *
1693
 * @since  3.2.0
1694
 * @param  array $value Number to add precision to.
1695
 * @param  bool  $round Should we round after adding precision?.
1696
 * @return int|array
1697
 */
1698
function wc_add_number_precision_deep( $value, $round = true ) {
1699 96
	if ( ! is_array( $value ) ) {
1700 96
		return wc_add_number_precision( $value, $round );
1701
	}
1702
1703 10
	foreach ( $value as $key => $sub_value ) {
1704 6
		$value[ $key ] = wc_add_number_precision_deep( $sub_value, $round );
1705
	}
1706
1707 10
	return $value;
1708
}
1709
1710
/**
1711
 * Remove precision from an array of number and return an array of int.
1712
 *
1713
 * @since  3.2.0
1714
 * @param  array $value Number to add precision to.
1715
 * @return int|array
1716
 */
1717
function wc_remove_number_precision_deep( $value ) {
1718 96
	if ( ! is_array( $value ) ) {
1719 95
		return wc_remove_number_precision( $value );
1720
	}
1721
1722 96
	foreach ( $value as $key => $sub_value ) {
1723 96
		$value[ $key ] = wc_remove_number_precision_deep( $sub_value );
1724
	}
1725
1726 96
	return $value;
1727
}
1728
1729
/**
1730
 * Get a shared logger instance.
1731
 *
1732
 * Use the woocommerce_logging_class filter to change the logging class. You may provide one of the following:
1733
 *     - a class name which will be instantiated as `new $class` with no arguments
1734
 *     - an instance which will be used directly as the logger
1735
 * In either case, the class or instance *must* implement WC_Logger_Interface.
1736
 *
1737
 * @see WC_Logger_Interface
1738
 *
1739
 * @return WC_Logger
1740
 */
1741
function wc_get_logger() {
1742 7
	static $logger = null;
1743
1744 7
	$class = apply_filters( 'woocommerce_logging_class', 'WC_Logger' );
1745
1746 7
	if ( null !== $logger && is_string( $class ) && is_a( $logger, $class ) ) {
1747 5
		return $logger;
1748
	}
1749
1750 3
	$implements = class_implements( $class );
1751
1752 3
	if ( is_array( $implements ) && in_array( 'WC_Logger_Interface', $implements, true ) ) {
1753 2
		$logger = is_object( $class ) ? $class : new $class();
1754
	} else {
1755 1
		wc_doing_it_wrong(
1756 1
			__FUNCTION__,
1757 1
			sprintf(
1758
				/* translators: 1: class name 2: woocommerce_logging_class 3: WC_Logger_Interface */
1759 1
				__( 'The class %1$s provided by %2$s filter must implement %3$s.', 'woocommerce' ),
1760 1
				'<code>' . esc_html( is_object( $class ) ? get_class( $class ) : $class ) . '</code>',
1761 1
				'<code>woocommerce_logging_class</code>',
1762 1
				'<code>WC_Logger_Interface</code>'
1763
			),
1764 1
			'3.0'
1765
		);
1766
1767 1
		$logger = is_a( $logger, 'WC_Logger' ) ? $logger : new WC_Logger();
1768
	}
1769
1770 3
	return $logger;
1771
}
1772
1773
/**
1774
 * Trigger logging cleanup using the logging class.
1775
 *
1776
 * @since 3.4.0
1777
 */
1778
function wc_cleanup_logs() {
1779
	$logger = wc_get_logger();
1780
1781
	if ( is_callable( array( $logger, 'clear_expired_logs' ) ) ) {
1782
		$logger->clear_expired_logs();
1783
	}
1784
}
1785
add_action( 'woocommerce_cleanup_logs', 'wc_cleanup_logs' );
1786
1787
/**
1788
 * Prints human-readable information about a variable.
1789
 *
1790
 * Some server environments blacklist some debugging functions. This function provides a safe way to
1791
 * turn an expression into a printable, readable form without calling blacklisted functions.
1792
 *
1793
 * @since 3.0
1794
 *
1795
 * @param mixed $expression The expression to be printed.
1796
 * @param bool  $return     Optional. Default false. Set to true to return the human-readable string.
1797
 * @return string|bool False if expression could not be printed. True if the expression was printed.
1798
 *     If $return is true, a string representation will be returned.
1799
 */
1800
function wc_print_r( $expression, $return = false ) {
1801
	$alternatives = array(
1802
		array(
1803 2
			'func' => 'print_r',
1804 2
			'args' => array( $expression, true ),
1805
		),
1806
		array(
1807 2
			'func' => 'var_export',
1808 2
			'args' => array( $expression, true ),
1809
		),
1810
		array(
1811 2
			'func' => 'json_encode',
1812 2
			'args' => array( $expression ),
1813
		),
1814
		array(
1815 2
			'func' => 'serialize',
1816 2
			'args' => array( $expression ),
1817
		),
1818
	);
1819
1820 2
	$alternatives = apply_filters( 'woocommerce_print_r_alternatives', $alternatives, $expression );
1821
1822 2
	foreach ( $alternatives as $alternative ) {
1823 2
		if ( function_exists( $alternative['func'] ) ) {
1824 2
			$res = call_user_func_array( $alternative['func'], $alternative['args'] );
1825 2
			if ( $return ) {
1826 2
				return $res;
1827
			}
1828
1829 1
			echo $res; // WPCS: XSS ok.
1830 1
			return true;
1831
		}
1832
	}
1833
1834 1
	return false;
1835
}
1836
1837
/**
1838
 * Registers the default log handler.
1839
 *
1840
 * @since 3.0
1841
 * @param array $handlers Handlers.
1842
 * @return array
1843
 */
1844
function wc_register_default_log_handler( $handlers ) {
1845 4
	if ( defined( 'WC_LOG_HANDLER' ) && class_exists( WC_LOG_HANDLER ) ) {
1846
		$handler_class   = WC_LOG_HANDLER;
1847
		$default_handler = new $handler_class();
1848
	} else {
1849 4
		$default_handler = new WC_Log_Handler_File();
1850
	}
1851
1852 4
	array_push( $handlers, $default_handler );
1853
1854 4
	return $handlers;
1855
}
1856
add_filter( 'woocommerce_register_log_handlers', 'wc_register_default_log_handler' );
1857
1858
/**
1859
 * Based on wp_list_pluck, this calls a method instead of returning a property.
1860
 *
1861
 * @since 3.0.0
1862
 * @param array      $list              List of objects or arrays.
1863
 * @param int|string $callback_or_field Callback method from the object to place instead of the entire object.
1864
 * @param int|string $index_key         Optional. Field from the object to use as keys for the new array.
1865
 *                                      Default null.
1866
 * @return array Array of values.
1867
 */
1868
function wc_list_pluck( $list, $callback_or_field, $index_key = null ) {
1869
	// Use wp_list_pluck if this isn't a callback.
1870 17
	$first_el = current( $list );
1871 17
	if ( ! is_object( $first_el ) || ! is_callable( array( $first_el, $callback_or_field ) ) ) {
1872 11
		return wp_list_pluck( $list, $callback_or_field, $index_key );
1873
	}
1874 17
	if ( ! $index_key ) {
1875
		/*
1876
		 * This is simple. Could at some point wrap array_column()
1877
		 * if we knew we had an array of arrays.
1878
		 */
1879 2
		foreach ( $list as $key => $value ) {
1880 2
			$list[ $key ] = $value->{$callback_or_field}();
1881
		}
1882 2
		return $list;
1883
	}
1884
1885
	/*
1886
	 * When index_key is not set for a particular item, push the value
1887
	 * to the end of the stack. This is how array_column() behaves.
1888
	 */
1889 15
	$newlist = array();
1890 15
	foreach ( $list as $value ) {
1891
		// Get index. @since 3.2.0 this supports a callback.
1892 15
		if ( is_callable( array( $value, $index_key ) ) ) {
1893 15
			$newlist[ $value->{$index_key}() ] = $value->{$callback_or_field}();
1894
		} elseif ( isset( $value->$index_key ) ) {
1895
			$newlist[ $value->$index_key ] = $value->{$callback_or_field}();
1896
		} else {
1897
			$newlist[] = $value->{$callback_or_field}();
1898
		}
1899
	}
1900 15
	return $newlist;
1901
}
1902
1903
/**
1904
 * Get permalink settings for things like products and taxonomies.
1905
 *
1906
 * As of 3.3.0, the permalink settings are stored to the option instead of
1907
 * being blank and inheritting from the locale. This speeds up page loading
1908
 * times by negating the need to switch locales on each page load.
1909
 *
1910
 * This is more inline with WP core behavior which does not localize slugs.
1911
 *
1912
 * @since  3.0.0
1913
 * @return array
1914
 */
1915
function wc_get_permalink_structure() {
1916 1
	$saved_permalinks = (array) get_option( 'woocommerce_permalinks', array() );
1917 1
	$permalinks       = wp_parse_args(
1918 1
		array_filter( $saved_permalinks ),
1919
		array(
1920 1
			'product_base'           => _x( 'product', 'slug', 'woocommerce' ),
1921 1
			'category_base'          => _x( 'product-category', 'slug', 'woocommerce' ),
1922 1
			'tag_base'               => _x( 'product-tag', 'slug', 'woocommerce' ),
1923 1
			'attribute_base'         => '',
1924
			'use_verbose_page_rules' => false,
1925
		)
1926
	);
1927
1928 1
	if ( $saved_permalinks !== $permalinks ) {
1929
		update_option( 'woocommerce_permalinks', $permalinks );
1930
	}
1931
1932 1
	$permalinks['product_rewrite_slug']   = untrailingslashit( $permalinks['product_base'] );
1933 1
	$permalinks['category_rewrite_slug']  = untrailingslashit( $permalinks['category_base'] );
1934 1
	$permalinks['tag_rewrite_slug']       = untrailingslashit( $permalinks['tag_base'] );
1935 1
	$permalinks['attribute_rewrite_slug'] = untrailingslashit( $permalinks['attribute_base'] );
1936
1937 1
	return $permalinks;
1938
}
1939
1940
/**
1941
 * Switch WooCommerce to site language.
1942
 *
1943
 * @since 3.1.0
1944
 */
1945
function wc_switch_to_site_locale() {
1946 1
	if ( function_exists( 'switch_to_locale' ) ) {
1947 1
		switch_to_locale( get_locale() );
1948
1949
		// Filter on plugin_locale so load_plugin_textdomain loads the correct locale.
1950 1
		add_filter( 'plugin_locale', 'get_locale' );
1951
1952
		// Init WC locale.
1953 1
		WC()->load_plugin_textdomain();
1954
	}
1955
}
1956
1957
/**
1958
 * Switch WooCommerce language to original.
1959
 *
1960
 * @since 3.1.0
1961
 */
1962
function wc_restore_locale() {
1963 1
	if ( function_exists( 'restore_previous_locale' ) ) {
1964 1
		restore_previous_locale();
1965
1966
		// Remove filter.
1967 1
		remove_filter( 'plugin_locale', 'get_locale' );
1968
1969
		// Init WC locale.
1970 1
		WC()->load_plugin_textdomain();
1971
	}
1972
}
1973
1974
/**
1975
 * Convert plaintext phone number to clickable phone number.
1976
 *
1977
 * Remove formatting and allow "+".
1978
 * Example and specs: https://developer.mozilla.org/en/docs/Web/HTML/Element/a#Creating_a_phone_link
1979
 *
1980
 * @since 3.1.0
1981
 *
1982
 * @param string $phone Content to convert phone number.
1983
 * @return string Content with converted phone number.
1984
 */
1985
function wc_make_phone_clickable( $phone ) {
1986
	$number = trim( preg_replace( '/[^\d|\+]/', '', $phone ) );
1987
1988
	return $number ? '<a href="tel:' . esc_attr( $number ) . '">' . esc_html( $phone ) . '</a>' : '';
1989
}
1990
1991
/**
1992
 * Get an item of post data if set, otherwise return a default value.
1993
 *
1994
 * @since  3.0.9
1995
 * @param  string $key     Meta key.
1996
 * @param  string $default Default value.
1997
 * @return mixed Value sanitized by wc_clean.
1998
 */
1999
function wc_get_post_data_by_key( $key, $default = '' ) {
2000
	return wc_clean( wp_unslash( wc_get_var( $_POST[ $key ], $default ) ) ); // @codingStandardsIgnoreLine
2001
}
2002
2003
/**
2004
 * Get data if set, otherwise return a default value or null. Prevents notices when data is not set.
2005
 *
2006
 * @since  3.2.0
2007
 * @param  mixed  $var     Variable.
2008
 * @param  string $default Default value.
2009
 * @return mixed
2010
 */
2011
function wc_get_var( &$var, $default = null ) {
2012
	return isset( $var ) ? $var : $default;
2013
}
2014
2015
/**
2016
 * Read in WooCommerce headers when reading plugin headers.
2017
 *
2018
 * @since 3.2.0
2019
 * @param array $headers Headers.
2020
 * @return array
2021
 */
2022
function wc_enable_wc_plugin_headers( $headers ) {
2023 21
	if ( ! class_exists( 'WC_Plugin_Updates' ) ) {
2024
		include_once dirname( __FILE__ ) . '/admin/plugin-updates/class-wc-plugin-updates.php';
2025
	}
2026
2027
	// WC requires at least - allows developers to define which version of WooCommerce the plugin requires to run.
2028 21
	$headers[] = WC_Plugin_Updates::VERSION_REQUIRED_HEADER;
2029
2030
	// WC tested up to - allows developers  to define which version of WooCommerce they have tested up to.
2031 21
	$headers[] = WC_Plugin_Updates::VERSION_TESTED_HEADER;
2032
2033
	// Woo - This is used in WooCommerce extensions and is picked up by the helper.
2034 21
	$headers[] = 'Woo';
2035
2036 21
	return $headers;
2037
}
2038
add_filter( 'extra_theme_headers', 'wc_enable_wc_plugin_headers' );
2039
add_filter( 'extra_plugin_headers', 'wc_enable_wc_plugin_headers' );
2040
2041
/**
2042
 * Prevent auto-updating the WooCommerce plugin on major releases if there are untested extensions active.
2043
 *
2044
 * @since 3.2.0
2045
 * @param  bool   $should_update If should update.
2046
 * @param  object $plugin        Plugin data.
2047
 * @return bool
2048
 */
2049
function wc_prevent_dangerous_auto_updates( $should_update, $plugin ) {
2050
	if ( ! isset( $plugin->plugin, $plugin->new_version ) ) {
2051
		return $should_update;
2052
	}
2053
2054
	if ( 'woocommerce/woocommerce.php' !== $plugin->plugin ) {
2055
		return $should_update;
2056
	}
2057
2058
	if ( ! class_exists( 'WC_Plugin_Updates' ) ) {
2059
		include_once dirname( __FILE__ ) . '/admin/plugin-updates/class-wc-plugin-updates.php';
2060
	}
2061
2062
	$new_version      = wc_clean( $plugin->new_version );
2063
	$plugin_updates   = new WC_Plugin_Updates();
2064
	$untested_plugins = $plugin_updates->get_untested_plugins( $new_version, 'major' );
0 ignored issues
show
Bug introduced by
It seems like $new_version defined by wc_clean($plugin->new_version) on line 2062 can also be of type array; however, WC_Plugin_Updates::get_untested_plugins() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2065
	if ( ! empty( $untested_plugins ) ) {
2066
		return false;
2067
	}
2068
2069
	return $should_update;
2070
}
2071
add_filter( 'auto_update_plugin', 'wc_prevent_dangerous_auto_updates', 99, 2 );
2072
2073
/**
2074
 * Delete expired transients.
2075
 *
2076
 * Deletes all expired transients. The multi-table delete syntax is used.
2077
 * to delete the transient record from table a, and the corresponding.
2078
 * transient_timeout record from table b.
2079
 *
2080
 * Based on code inside core's upgrade_network() function.
2081
 *
2082
 * @since 3.2.0
2083
 * @return int Number of transients that were cleared.
2084
 */
2085
function wc_delete_expired_transients() {
2086
	global $wpdb;
2087
2088 2
	$sql  = "DELETE a, b FROM $wpdb->options a, $wpdb->options b
2089
		WHERE a.option_name LIKE %s
2090
		AND a.option_name NOT LIKE %s
2091
		AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
2092
		AND b.option_value < %d";
2093 2
	$rows = $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) ); // WPCS: unprepared SQL ok.
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
2094
2095 2
	$sql   = "DELETE a, b FROM $wpdb->options a, $wpdb->options b
2096
		WHERE a.option_name LIKE %s
2097
		AND a.option_name NOT LIKE %s
2098
		AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) )
2099
		AND b.option_value < %d";
2100 2
	$rows2 = $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_site_transient_' ) . '%', $wpdb->esc_like( '_site_transient_timeout_' ) . '%', time() ) ); // WPCS: unprepared SQL ok.
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
2101
2102 2
	return absint( $rows + $rows2 );
2103
}
2104
add_action( 'woocommerce_installed', 'wc_delete_expired_transients' );
2105
2106
/**
2107
 * Make a URL relative, if possible.
2108
 *
2109
 * @since 3.2.0
2110
 * @param string $url URL to make relative.
2111
 * @return string
2112
 */
2113
function wc_get_relative_url( $url ) {
2114
	return wc_is_external_resource( $url ) ? $url : str_replace( array( 'http://', 'https://' ), '//', $url );
2115
}
2116
2117
/**
2118
 * See if a resource is remote.
2119
 *
2120
 * @since 3.2.0
2121
 * @param string $url URL to check.
2122
 * @return bool
2123
 */
2124
function wc_is_external_resource( $url ) {
2125
	$wp_base = str_replace( array( 'http://', 'https://' ), '//', get_home_url( null, '/', 'http' ) );
2126
2127
	return strstr( $url, '://' ) && ! strstr( $url, $wp_base );
2128
}
2129
2130
/**
2131
 * See if theme/s is activate or not.
2132
 *
2133
 * @since 3.3.0
2134
 * @param string|array $theme Theme name or array of theme names to check.
2135
 * @return boolean
2136
 */
2137
function wc_is_active_theme( $theme ) {
2138 1
	return is_array( $theme ) ? in_array( get_template(), $theme, true ) : get_template() === $theme;
2139
}
2140
2141
/**
2142
 * Cleans up session data - cron callback.
2143
 *
2144
 * @since 3.3.0
2145
 */
2146
function wc_cleanup_session_data() {
2147
	$session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );
2148
	$session       = new $session_class();
2149
2150
	if ( is_callable( array( $session, 'cleanup_sessions' ) ) ) {
2151
		$session->cleanup_sessions();
2152
	}
2153
}
2154
add_action( 'woocommerce_cleanup_sessions', 'wc_cleanup_session_data' );
2155
2156
/**
2157
 * Convert a decimal (e.g. 3.5) to a fraction (e.g. 7/2).
2158
 * From: https://www.designedbyaturtle.co.uk/2015/converting-a-decimal-to-a-fraction-in-php/
2159
 *
2160
 * @param float $decimal the decimal number.
2161
 * @return array|bool a 1/2 would be [1, 2] array (this can be imploded with '/' to form a string).
2162
 */
2163
function wc_decimal_to_fraction( $decimal ) {
2164 1
	if ( 0 > $decimal || ! is_numeric( $decimal ) ) {
2165
		// Negative digits need to be passed in as positive numbers and prefixed as negative once the response is imploded.
2166
		return false;
2167
	}
2168
2169 1
	if ( 0 === $decimal ) {
2170
		return array( 0, 1 );
2171
	}
2172
2173 1
	$tolerance   = 1.e-4;
2174 1
	$numerator   = 1;
2175 1
	$h2          = 0;
2176 1
	$denominator = 0;
2177 1
	$k2          = 1;
2178 1
	$b           = 1 / $decimal;
2179
2180
	do {
2181 1
		$b           = 1 / $b;
2182 1
		$a           = floor( $b );
2183 1
		$aux         = $numerator;
2184 1
		$numerator   = $a * $numerator + $h2;
2185 1
		$h2          = $aux;
2186 1
		$aux         = $denominator;
2187 1
		$denominator = $a * $denominator + $k2;
2188 1
		$k2          = $aux;
2189 1
		$b           = $b - $a;
2190 1
	} while ( abs( $decimal - $numerator / $denominator ) > $decimal * $tolerance );
2191
2192 1
	return array( $numerator, $denominator );
2193
}
2194
2195
/**
2196
 * Round discount.
2197
 *
2198
 * @param  double $value Amount to round.
2199
 * @param  int    $precision DP to round.
2200
 * @return float
2201
 */
2202
function wc_round_discount( $value, $precision ) {
2203 87
	if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
2204 87
		return round( $value, $precision, WC_DISCOUNT_ROUNDING_MODE ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.round_modeFound
2205
	}
2206
2207
	if ( 2 === WC_DISCOUNT_ROUNDING_MODE ) {
2208
		return wc_legacy_round_half_down( $value, $precision );
2209
	}
2210
2211
	return round( $value, $precision );
2212
}
2213
2214
/**
2215
 * Return the html selected attribute if stringified $value is found in array of stringified $options
2216
 * or if stringified $value is the same as scalar stringified $options.
2217
 *
2218
 * @param string|int       $value   Value to find within options.
2219
 * @param string|int|array $options Options to go through when looking for value.
2220
 * @return string
2221
 */
2222
function wc_selected( $value, $options ) {
2223 1 View Code Duplication
	if ( is_array( $options ) ) {
2224 1
		$options = array_map( 'strval', $options );
2225 1
		return selected( in_array( (string) $value, $options, true ), true, false );
2226
	}
2227
2228 1
	return selected( $value, $options, false );
2229
}
2230
2231
/**
2232
 * Retrieves the MySQL server version. Based on $wpdb.
2233
 *
2234
 * @since 3.4.1
2235
 * @return array Vesion information.
2236
 */
2237
function wc_get_server_database_version() {
2238
	global $wpdb;
2239
2240 16
	if ( empty( $wpdb->is_mysql ) ) {
2241
		return array(
2242
			'string' => '',
2243
			'number' => '',
2244
		);
2245
	}
2246
2247 16
	if ( $wpdb->use_mysqli ) {
2248 16
		$server_info = mysqli_get_server_info( $wpdb->dbh ); // @codingStandardsIgnoreLine.
2249
	} else {
2250
		$server_info = mysql_get_server_info( $wpdb->dbh ); // @codingStandardsIgnoreLine.
2251
	}
2252
2253
	return array(
2254 16
		'string' => $server_info,
2255 16
		'number' => preg_replace( '/([^\d.]+).*/', '', $server_info ),
2256
	);
2257
}
2258
2259
/**
2260
 * Initialize and load the cart functionality.
2261
 *
2262
 * @since 3.6.4
2263
 * @return void
2264
 */
2265
function wc_load_cart() {
2266 1
	WC()->initialize_session();
2267 1
	WC()->initialize_cart();
2268
}
2269