Completed
Pull Request — master (#11429)
by Leon
09:57
created

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

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 4
c 1
b 1
f 0
nc 2
nop 0
dl 0
loc 7
rs 9.4285
1
<?php
2
/**
3
 * WooCommerce Core Functions
4
 *
5
 * General core functions available on both the front-end and admin.
6
 *
7
 * @author 		WooThemes
8
 * @category 	Core
9
 * @package 	WooCommerce/Functions
10
 * @version     2.1.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
// Include core functions (available in both admin and frontend).
18
include( 'wc-conditional-functions.php' );
19
include( 'wc-coupon-functions.php' );
20
include( 'wc-user-functions.php' );
21
include( 'wc-deprecated-functions.php' );
22
include( 'wc-formatting-functions.php' );
23
include( 'wc-order-functions.php' );
24
include( 'wc-page-functions.php' );
25
include( 'wc-product-functions.php' );
26
include( 'wc-account-functions.php' );
27
include( 'wc-term-functions.php' );
28
include( 'wc-attribute-functions.php' );
29
include( 'wc-rest-functions.php' );
30
31
/**
32
 * Filters on data used in admin and frontend.
33
 */
34
add_filter( 'woocommerce_coupon_code', 'html_entity_decode' );
35
add_filter( 'woocommerce_coupon_code', 'sanitize_text_field' );
36
add_filter( 'woocommerce_coupon_code', 'strtolower' ); // Coupons case-insensitive by default
37
add_filter( 'woocommerce_stock_amount', 'intval' ); // Stock amounts are integers by default
38
add_filter( 'woocommerce_shipping_rate_label', 'sanitize_text_field' ); // Shipping rate label
39
40
/**
41
 * Short Description (excerpt).
42
 */
43
add_filter( 'woocommerce_short_description', 'wptexturize' );
44
add_filter( 'woocommerce_short_description', 'convert_smilies' );
45
add_filter( 'woocommerce_short_description', 'convert_chars' );
46
add_filter( 'woocommerce_short_description', 'wpautop' );
47
add_filter( 'woocommerce_short_description', 'shortcode_unautop' );
48
add_filter( 'woocommerce_short_description', 'prepend_attachment' );
49
add_filter( 'woocommerce_short_description', 'do_shortcode', 11 ); // AFTER wpautop()
50
51
/**
52
 * Create a new order programmatically.
53
 *
54
 * Returns a new order object on success which can then be used to add additional data.
55
 *
56
 * @param  array $args
57
 *
58
 * @return WC_Order|WP_Error WC_Order on success, WP_Error on failure.
59
 */
60
function wc_create_order( $args = array() ) {
61
	$default_args = array(
62
		'status'        => '',
63
		'customer_id'   => null,
64
		'customer_note' => null,
65
		'order_id'      => 0,
66
		'created_via'   => '',
67
		'cart_hash'     => '',
68
		'parent'        => 0,
69
	);
70
71
	$args       = wp_parse_args( $args, $default_args );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $args. This often makes code more readable.
Loading history...
72
	$order_data = array();
73
74
	if ( $args['order_id'] > 0 ) {
75
		$updating         = true;
76
		$order_data['ID'] = $args['order_id'];
77
	} else {
78
		$updating                    = false;
79
		$order_data['post_type']     = 'shop_order';
80
		$order_data['post_status']   = 'wc-' . apply_filters( 'woocommerce_default_order_status', 'pending' );
81
		$order_data['ping_status']   = 'closed';
82
		$order_data['post_author']   = 1;
83
		$order_data['post_password'] = uniqid( 'order_' );
84
		$order_data['post_title']    = sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) );
85
		$order_data['post_parent']   = absint( $args['parent'] );
86
	}
87
88
	if ( $args['status'] ) {
89
		if ( ! in_array( 'wc-' . $args['status'], array_keys( wc_get_order_statuses() ) ) ) {
90
			return new WP_Error( 'woocommerce_invalid_order_status', __( 'Invalid order status', 'woocommerce' ) );
91
		}
92
		$order_data['post_status']  = 'wc-' . $args['status'];
93
	}
94
95
	if ( ! is_null( $args['customer_note'] ) ) {
96
		$order_data['post_excerpt'] = $args['customer_note'];
97
	}
98
99
	if ( $updating ) {
100
		$order_id = wp_update_post( $order_data );
101
	} else {
102
		$order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', $order_data ), true );
103
	}
104
105
	if ( is_wp_error( $order_id ) ) {
106
		return $order_id;
107
	}
108
109
	if ( ! $updating ) {
110
		update_post_meta( $order_id, '_order_key', 'wc_' . apply_filters( 'woocommerce_generate_order_key', uniqid( 'order_' ) ) );
111
		update_post_meta( $order_id, '_order_currency', get_woocommerce_currency() );
112
		update_post_meta( $order_id, '_prices_include_tax', get_option( 'woocommerce_prices_include_tax' ) );
113
		update_post_meta( $order_id, '_customer_ip_address', WC_Geolocation::get_ip_address() );
114
		update_post_meta( $order_id, '_customer_user_agent', isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '' );
115
		update_post_meta( $order_id, '_customer_user', 0 );
116
		update_post_meta( $order_id, '_created_via', sanitize_text_field( $args['created_via'] ) );
117
		update_post_meta( $order_id, '_cart_hash', sanitize_text_field( $args['cart_hash'] ) );
118
	}
119
120
	if ( is_numeric( $args['customer_id'] ) ) {
121
		update_post_meta( $order_id, '_customer_user', $args['customer_id'] );
122
	}
123
124
	update_post_meta( $order_id, '_order_version', WC_VERSION );
125
126
	return wc_get_order( $order_id );
127
}
128
129
/**
130
 * Update an order. Uses wc_create_order.
131
 *
132
 * @param  array $args
133
 * @return string | WC_Order
134
 */
135
function wc_update_order( $args ) {
136
	if ( ! $args['order_id'] ) {
137
		return new WP_Error( __( 'Invalid order ID', 'woocommerce' ) );
138
	}
139
	return wc_create_order( $args );
140
}
141
142
/**
143
 * Get template part (for templates like the shop-loop).
144
 *
145
 * WC_TEMPLATE_DEBUG_MODE will prevent overrides in themes from taking priority.
146
 *
147
 * @access public
148
 * @param mixed $slug
149
 * @param string $name (default: '')
150
 */
151
function wc_get_template_part( $slug, $name = '' ) {
152
	$template = '';
153
154
	// Look in yourtheme/slug-name.php and yourtheme/woocommerce/slug-name.php
155 View Code Duplication
	if ( $name && ! WC_TEMPLATE_DEBUG_MODE ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
156
		$template = locate_template( array( "{$slug}-{$name}.php", WC()->template_path() . "{$slug}-{$name}.php" ) );
157
	}
158
159
	// Get default slug-name.php
160
	if ( ! $template && $name && file_exists( WC()->plugin_path() . "/templates/{$slug}-{$name}.php" ) ) {
161
		$template = WC()->plugin_path() . "/templates/{$slug}-{$name}.php";
162
	}
163
164
	// If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php
165 View Code Duplication
	if ( ! $template && ! WC_TEMPLATE_DEBUG_MODE ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
166
		$template = locate_template( array( "{$slug}.php", WC()->template_path() . "{$slug}.php" ) );
167
	}
168
169
	// Allow 3rd party plugins to filter template file from their plugin.
170
	$template = apply_filters( 'wc_get_template_part', $template, $slug, $name );
171
172
	if ( $template ) {
173
		load_template( $template, false );
174
	}
175
}
176
177
/**
178
 * Get other templates (e.g. product attributes) passing attributes and including the file.
179
 *
180
 * @access public
181
 * @param string $template_name
182
 * @param array $args (default: array())
183
 * @param string $template_path (default: '')
184
 * @param string $default_path (default: '')
185
 */
186
function wc_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
187
	if ( ! empty( $args ) && is_array( $args ) ) {
188
		extract( $args );
189
	}
190
191
	$located = wc_locate_template( $template_name, $template_path, $default_path );
192
193
	if ( ! file_exists( $located ) ) {
194
		_doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', $located ), '2.1' );
195
		return;
196
	}
197
198
	// Allow 3rd party plugin filter template file from their plugin.
199
	$located = apply_filters( 'wc_get_template', $located, $template_name, $args, $template_path, $default_path );
200
201
	do_action( 'woocommerce_before_template_part', $template_name, $template_path, $located, $args );
202
203
	include( $located );
204
205
	do_action( 'woocommerce_after_template_part', $template_name, $template_path, $located, $args );
206
}
207
208
/**
209
 * Like wc_get_template, but returns the HTML instead of outputting.
210
 * @see wc_get_template
211
 * @since 2.5.0
212
 * @param string $template_name
213
 */
214
function wc_get_template_html( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
215
	ob_start();
216
	wc_get_template( $template_name, $args, $template_path, $default_path );
217
	return ob_get_clean();
218
}
219
220
/**
221
 * Locate a template and return the path for inclusion.
222
 *
223
 * This is the load order:
224
 *
225
 *		yourtheme		/	$template_path	/	$template_name
226
 *		yourtheme		/	$template_name
227
 *		$default_path	/	$template_name
228
 *
229
 * @access public
230
 * @param string $template_name
231
 * @param string $template_path (default: '')
232
 * @param string $default_path (default: '')
233
 * @return string
234
 */
235
function wc_locate_template( $template_name, $template_path = '', $default_path = '' ) {
236
	if ( ! $template_path ) {
237
		$template_path = WC()->template_path();
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $template_path. This often makes code more readable.
Loading history...
238
	}
239
240
	if ( ! $default_path ) {
241
		$default_path = WC()->plugin_path() . '/templates/';
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $default_path. This often makes code more readable.
Loading history...
242
	}
243
244
	// Look within passed path within the theme - this is priority.
245
	$template = locate_template(
246
		array(
247
			trailingslashit( $template_path ) . $template_name,
248
			$template_name
249
		)
250
	);
251
252
	// Get default template/
253
	if ( ! $template || WC_TEMPLATE_DEBUG_MODE ) {
254
		$template = $default_path . $template_name;
255
	}
256
257
	// Return what we found.
258
	return apply_filters( 'woocommerce_locate_template', $template, $template_name, $template_path );
259
}
260
261
/**
262
 * Get Base Currency Code.
263
 *
264
 * @return string
265
 */
266
function get_woocommerce_currency() {
267
	return apply_filters( 'woocommerce_currency', get_option('woocommerce_currency') );
268
}
269
270
/**
271
 * Get full list of currency codes.
272
 *
273
 * @return array
274
 */
275
function get_woocommerce_currencies() {
276
	return array_unique(
277
		apply_filters( 'woocommerce_currencies',
278
			array(
279
				'AED' => __( 'United Arab Emirates dirham', 'woocommerce' ),
280
				'AFN' => __( 'Afghan afghani', 'woocommerce' ),
281
				'ALL' => __( 'Albanian lek', 'woocommerce' ),
282
				'AMD' => __( 'Armenian dram', 'woocommerce' ),
283
				'ANG' => __( 'Netherlands Antillean guilder', 'woocommerce' ),
284
				'AOA' => __( 'Angolan kwanza', 'woocommerce' ),
285
				'ARS' => __( 'Argentine peso', 'woocommerce' ),
286
				'AUD' => __( 'Australian dollar', 'woocommerce' ),
287
				'AWG' => __( 'Aruban florin', 'woocommerce' ),
288
				'AZN' => __( 'Azerbaijani manat', 'woocommerce' ),
289
				'BAM' => __( 'Bosnia and Herzegovina convertible mark', 'woocommerce' ),
290
				'BBD' => __( 'Barbadian dollar', 'woocommerce' ),
291
				'BDT' => __( 'Bangladeshi taka', 'woocommerce' ),
292
				'BGN' => __( 'Bulgarian lev', 'woocommerce' ),
293
				'BHD' => __( 'Bahraini dinar', 'woocommerce' ),
294
				'BIF' => __( 'Burundian franc', 'woocommerce' ),
295
				'BMD' => __( 'Bermudian dollar', 'woocommerce' ),
296
				'BND' => __( 'Brunei dollar', 'woocommerce' ),
297
				'BOB' => __( 'Bolivian boliviano', 'woocommerce' ),
298
				'BRL' => __( 'Brazilian real', 'woocommerce' ),
299
				'BSD' => __( 'Bahamian dollar', 'woocommerce' ),
300
				'BTC' => __( 'Bitcoin', 'woocommerce' ),
301
				'BTN' => __( 'Bhutanese ngultrum', 'woocommerce' ),
302
				'BWP' => __( 'Botswana pula', 'woocommerce' ),
303
				'BYR' => __( 'Belarusian ruble', 'woocommerce' ),
304
				'BZD' => __( 'Belize dollar', 'woocommerce' ),
305
				'CAD' => __( 'Canadian dollar', 'woocommerce' ),
306
				'CDF' => __( 'Congolese franc', 'woocommerce' ),
307
				'CHF' => __( 'Swiss franc', 'woocommerce' ),
308
				'CLP' => __( 'Chilean peso', 'woocommerce' ),
309
				'CNY' => __( 'Chinese yuan', 'woocommerce' ),
310
				'COP' => __( 'Colombian peso', 'woocommerce' ),
311
				'CRC' => __( 'Costa Rican col&oacute;n', 'woocommerce' ),
312
				'CUC' => __( 'Cuban convertible peso', 'woocommerce' ),
313
				'CUP' => __( 'Cuban peso', 'woocommerce' ),
314
				'CVE' => __( 'Cape Verdean escudo', 'woocommerce' ),
315
				'CZK' => __( 'Czech koruna', 'woocommerce' ),
316
				'DJF' => __( 'Djiboutian franc', 'woocommerce' ),
317
				'DKK' => __( 'Danish krone', 'woocommerce' ),
318
				'DOP' => __( 'Dominican peso', 'woocommerce' ),
319
				'DZD' => __( 'Algerian dinar', 'woocommerce' ),
320
				'EGP' => __( 'Egyptian pound', 'woocommerce' ),
321
				'ERN' => __( 'Eritrean nakfa', 'woocommerce' ),
322
				'ETB' => __( 'Ethiopian birr', 'woocommerce' ),
323
				'EUR' => __( 'Euro', 'woocommerce' ),
324
				'FJD' => __( 'Fijian dollar', 'woocommerce' ),
325
				'FKP' => __( 'Falkland Islands pound', 'woocommerce' ),
326
				'GBP' => __( 'Pound sterling', 'woocommerce' ),
327
				'GEL' => __( 'Georgian lari', 'woocommerce' ),
328
				'GGP' => __( 'Guernsey pound', 'woocommerce' ),
329
				'GHS' => __( 'Ghana cedi', 'woocommerce' ),
330
				'GIP' => __( 'Gibraltar pound', 'woocommerce' ),
331
				'GMD' => __( 'Gambian dalasi', 'woocommerce' ),
332
				'GNF' => __( 'Guinean franc', 'woocommerce' ),
333
				'GTQ' => __( 'Guatemalan quetzal', 'woocommerce' ),
334
				'GYD' => __( 'Guyanese dollar', 'woocommerce' ),
335
				'HKD' => __( 'Hong Kong dollar', 'woocommerce' ),
336
				'HNL' => __( 'Honduran lempira', 'woocommerce' ),
337
				'HRK' => __( 'Croatian kuna', 'woocommerce' ),
338
				'HTG' => __( 'Haitian gourde', 'woocommerce' ),
339
				'HUF' => __( 'Hungarian forint', 'woocommerce' ),
340
				'IDR' => __( 'Indonesian rupiah', 'woocommerce' ),
341
				'ILS' => __( 'Israeli new shekel', 'woocommerce' ),
342
				'IMP' => __( 'Manx pound', 'woocommerce' ),
343
				'INR' => __( 'Indian rupee', 'woocommerce' ),
344
				'IQD' => __( 'Iraqi dinar', 'woocommerce' ),
345
				'IRR' => __( 'Iranian rial', 'woocommerce' ),
346
				'ISK' => __( 'Icelandic kr&oacute;na', 'woocommerce' ),
347
				'JEP' => __( 'Jersey pound', 'woocommerce' ),
348
				'JMD' => __( 'Jamaican dollar', 'woocommerce' ),
349
				'JOD' => __( 'Jordanian dinar', 'woocommerce' ),
350
				'JPY' => __( 'Japanese yen', 'woocommerce' ),
351
				'KES' => __( 'Kenyan shilling', 'woocommerce' ),
352
				'KGS' => __( 'Kyrgyzstani som', 'woocommerce' ),
353
				'KHR' => __( 'Cambodian riel', 'woocommerce' ),
354
				'KMF' => __( 'Comorian franc', 'woocommerce' ),
355
				'KPW' => __( 'North Korean won', 'woocommerce' ),
356
				'KRW' => __( 'South Korean won', 'woocommerce' ),
357
				'KWD' => __( 'Kuwaiti dinar', 'woocommerce' ),
358
				'KYD' => __( 'Cayman Islands dollar', 'woocommerce' ),
359
				'KZT' => __( 'Kazakhstani tenge', 'woocommerce' ),
360
				'LAK' => __( 'Lao kip', 'woocommerce' ),
361
				'LBP' => __( 'Lebanese pound', 'woocommerce' ),
362
				'LKR' => __( 'Sri Lankan rupee', 'woocommerce' ),
363
				'LRD' => __( 'Liberian dollar', 'woocommerce' ),
364
				'LSL' => __( 'Lesotho loti', 'woocommerce' ),
365
				'LYD' => __( 'Libyan dinar', 'woocommerce' ),
366
				'MAD' => __( 'Moroccan dirham', 'woocommerce' ),
367
				'MDL' => __( 'Moldovan leu', 'woocommerce' ),
368
				'MGA' => __( 'Malagasy ariary', 'woocommerce' ),
369
				'MKD' => __( 'Macedonian denar', 'woocommerce' ),
370
				'MMK' => __( 'Burmese kyat', 'woocommerce' ),
371
				'MNT' => __( 'Mongolian t&ouml;gr&ouml;g', 'woocommerce' ),
372
				'MOP' => __( 'Macanese pataca', 'woocommerce' ),
373
				'MRO' => __( 'Mauritanian ouguiya', 'woocommerce' ),
374
				'MUR' => __( 'Mauritian rupee', 'woocommerce' ),
375
				'MVR' => __( 'Maldivian rufiyaa', 'woocommerce' ),
376
				'MWK' => __( 'Malawian kwacha', 'woocommerce' ),
377
				'MXN' => __( 'Mexican peso', 'woocommerce' ),
378
				'MYR' => __( 'Malaysian ringgit', 'woocommerce' ),
379
				'MZN' => __( 'Mozambican metical', 'woocommerce' ),
380
				'NAD' => __( 'Namibian dollar', 'woocommerce' ),
381
				'NGN' => __( 'Nigerian naira', 'woocommerce' ),
382
				'NIO' => __( 'Nicaraguan c&oacute;rdoba', 'woocommerce' ),
383
				'NOK' => __( 'Norwegian krone', 'woocommerce' ),
384
				'NPR' => __( 'Nepalese rupee', 'woocommerce' ),
385
				'NZD' => __( 'New Zealand dollar', 'woocommerce' ),
386
				'OMR' => __( 'Omani rial', 'woocommerce' ),
387
				'PAB' => __( 'Panamanian balboa', 'woocommerce' ),
388
				'PEN' => __( 'Peruvian nuevo sol', 'woocommerce' ),
389
				'PGK' => __( 'Papua New Guinean kina', 'woocommerce' ),
390
				'PHP' => __( 'Philippine peso', 'woocommerce' ),
391
				'PKR' => __( 'Pakistani rupee', 'woocommerce' ),
392
				'PLN' => __( 'Polish z&#x142;oty', 'woocommerce' ),
393
				'PRB' => __( 'Transnistrian ruble', 'woocommerce' ),
394
				'PYG' => __( 'Paraguayan guaran&iacute;', 'woocommerce' ),
395
				'QAR' => __( 'Qatari riyal', 'woocommerce' ),
396
				'RON' => __( 'Romanian leu', 'woocommerce' ),
397
				'RSD' => __( 'Serbian dinar', 'woocommerce' ),
398
				'RUB' => __( 'Russian ruble', 'woocommerce' ),
399
				'RWF' => __( 'Rwandan franc', 'woocommerce' ),
400
				'SAR' => __( 'Saudi riyal', 'woocommerce' ),
401
				'SBD' => __( 'Solomon Islands dollar', 'woocommerce' ),
402
				'SCR' => __( 'Seychellois rupee', 'woocommerce' ),
403
				'SDG' => __( 'Sudanese pound', 'woocommerce' ),
404
				'SEK' => __( 'Swedish krona', 'woocommerce' ),
405
				'SGD' => __( 'Singapore dollar', 'woocommerce' ),
406
				'SHP' => __( 'Saint Helena pound', 'woocommerce' ),
407
				'SLL' => __( 'Sierra Leonean leone', 'woocommerce' ),
408
				'SOS' => __( 'Somali shilling', 'woocommerce' ),
409
				'SRD' => __( 'Surinamese dollar', 'woocommerce' ),
410
				'SSP' => __( 'South Sudanese pound', 'woocommerce' ),
411
				'STD' => __( 'S&atilde;o Tom&eacute; and Pr&iacute;ncipe dobra', 'woocommerce' ),
412
				'SYP' => __( 'Syrian pound', 'woocommerce' ),
413
				'SZL' => __( 'Swazi lilangeni', 'woocommerce' ),
414
				'THB' => __( 'Thai baht', 'woocommerce' ),
415
				'TJS' => __( 'Tajikistani somoni', 'woocommerce' ),
416
				'TMT' => __( 'Turkmenistan manat', 'woocommerce' ),
417
				'TND' => __( 'Tunisian dinar', 'woocommerce' ),
418
				'TOP' => __( 'Tongan pa&#x2bb;anga', 'woocommerce' ),
419
				'TRY' => __( 'Turkish lira', 'woocommerce' ),
420
				'TTD' => __( 'Trinidad and Tobago dollar', 'woocommerce' ),
421
				'TWD' => __( 'New Taiwan dollar', 'woocommerce' ),
422
				'TZS' => __( 'Tanzanian shilling', 'woocommerce' ),
423
				'UAH' => __( 'Ukrainian hryvnia', 'woocommerce' ),
424
				'UGX' => __( 'Ugandan shilling', 'woocommerce' ),
425
				'USD' => __( 'United States dollar', 'woocommerce' ),
426
				'UYU' => __( 'Uruguayan peso', 'woocommerce' ),
427
				'UZS' => __( 'Uzbekistani som', 'woocommerce' ),
428
				'VEF' => __( 'Venezuelan bol&iacute;var', 'woocommerce' ),
429
				'VND' => __( 'Vietnamese &#x111;&#x1ed3;ng', 'woocommerce' ),
430
				'VUV' => __( 'Vanuatu vatu', 'woocommerce' ),
431
				'WST' => __( 'Samoan t&#x101;l&#x101;', 'woocommerce' ),
432
				'XAF' => __( 'Central African CFA franc', 'woocommerce' ),
433
				'XCD' => __( 'East Caribbean dollar', 'woocommerce' ),
434
				'XOF' => __( 'West African CFA franc', 'woocommerce' ),
435
				'XPF' => __( 'CFP franc', 'woocommerce' ),
436
				'YER' => __( 'Yemeni rial', 'woocommerce' ),
437
				'ZAR' => __( 'South African rand', 'woocommerce' ),
438
				'ZMW' => __( 'Zambian kwacha', 'woocommerce' ),
439
			)
440
		)
441
	);
442
}
443
444
/**
445
 * Get Currency symbol.
446
 *
447
 * @param string $currency (default: '')
448
 * @return string
449
 */
450
function get_woocommerce_currency_symbol( $currency = '' ) {
451
	if ( ! $currency ) {
452
		$currency = get_woocommerce_currency();
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $currency. This often makes code more readable.
Loading history...
453
	}
454
455
	$symbols = apply_filters( 'woocommerce_currency_symbols', array(
456
		'AED' => '&#x62f;.&#x625;',
457
		'AFN' => '&#x60b;',
458
		'ALL' => 'L',
459
		'AMD' => 'AMD',
460
		'ANG' => '&fnof;',
461
		'AOA' => 'Kz',
462
		'ARS' => '&#36;',
463
		'AUD' => '&#36;',
464
		'AWG' => '&fnof;',
465
		'AZN' => 'AZN',
466
		'BAM' => 'KM',
467
		'BBD' => '&#36;',
468
		'BDT' => '&#2547;&nbsp;',
469
		'BGN' => '&#1083;&#1074;.',
470
		'BHD' => '.&#x62f;.&#x628;',
471
		'BIF' => 'Fr',
472
		'BMD' => '&#36;',
473
		'BND' => '&#36;',
474
		'BOB' => 'Bs.',
475
		'BRL' => '&#82;&#36;',
476
		'BSD' => '&#36;',
477
		'BTC' => '&#3647;',
478
		'BTN' => 'Nu.',
479
		'BWP' => 'P',
480
		'BYR' => 'Br',
481
		'BZD' => '&#36;',
482
		'CAD' => '&#36;',
483
		'CDF' => 'Fr',
484
		'CHF' => '&#67;&#72;&#70;',
485
		'CLP' => '&#36;',
486
		'CNY' => '&yen;',
487
		'COP' => '&#36;',
488
		'CRC' => '&#x20a1;',
489
		'CUC' => '&#36;',
490
		'CUP' => '&#36;',
491
		'CVE' => '&#36;',
492
		'CZK' => '&#75;&#269;',
493
		'DJF' => 'Fr',
494
		'DKK' => 'DKK',
495
		'DOP' => 'RD&#36;',
496
		'DZD' => '&#x62f;.&#x62c;',
497
		'EGP' => 'EGP',
498
		'ERN' => 'Nfk',
499
		'ETB' => 'Br',
500
		'EUR' => '&euro;',
501
		'FJD' => '&#36;',
502
		'FKP' => '&pound;',
503
		'GBP' => '&pound;',
504
		'GEL' => '&#x10da;',
505
		'GGP' => '&pound;',
506
		'GHS' => '&#x20b5;',
507
		'GIP' => '&pound;',
508
		'GMD' => 'D',
509
		'GNF' => 'Fr',
510
		'GTQ' => 'Q',
511
		'GYD' => '&#36;',
512
		'HKD' => '&#36;',
513
		'HNL' => 'L',
514
		'HRK' => 'Kn',
515
		'HTG' => 'G',
516
		'HUF' => '&#70;&#116;',
517
		'IDR' => 'Rp',
518
		'ILS' => '&#8362;',
519
		'IMP' => '&pound;',
520
		'INR' => '&#8377;',
521
		'IQD' => '&#x639;.&#x62f;',
522
		'IRR' => '&#xfdfc;',
523
		'ISK' => 'Kr.',
524
		'JEP' => '&pound;',
525
		'JMD' => '&#36;',
526
		'JOD' => '&#x62f;.&#x627;',
527
		'JPY' => '&yen;',
528
		'KES' => 'KSh',
529
		'KGS' => '&#x43b;&#x432;',
530
		'KHR' => '&#x17db;',
531
		'KMF' => 'Fr',
532
		'KPW' => '&#x20a9;',
533
		'KRW' => '&#8361;',
534
		'KWD' => '&#x62f;.&#x643;',
535
		'KYD' => '&#36;',
536
		'KZT' => 'KZT',
537
		'LAK' => '&#8365;',
538
		'LBP' => '&#x644;.&#x644;',
539
		'LKR' => '&#xdbb;&#xdd4;',
540
		'LRD' => '&#36;',
541
		'LSL' => 'L',
542
		'LYD' => '&#x644;.&#x62f;',
543
		'MAD' => '&#x62f;. &#x645;.',
544
		'MAD' => '&#x62f;.&#x645;.',
545
		'MDL' => 'L',
546
		'MGA' => 'Ar',
547
		'MKD' => '&#x434;&#x435;&#x43d;',
548
		'MMK' => 'Ks',
549
		'MNT' => '&#x20ae;',
550
		'MOP' => 'P',
551
		'MRO' => 'UM',
552
		'MUR' => '&#x20a8;',
553
		'MVR' => '.&#x783;',
554
		'MWK' => 'MK',
555
		'MXN' => '&#36;',
556
		'MYR' => '&#82;&#77;',
557
		'MZN' => 'MT',
558
		'NAD' => '&#36;',
559
		'NGN' => '&#8358;',
560
		'NIO' => 'C&#36;',
561
		'NOK' => '&#107;&#114;',
562
		'NPR' => '&#8360;',
563
		'NZD' => '&#36;',
564
		'OMR' => '&#x631;.&#x639;.',
565
		'PAB' => 'B/.',
566
		'PEN' => 'S/.',
567
		'PGK' => 'K',
568
		'PHP' => '&#8369;',
569
		'PKR' => '&#8360;',
570
		'PLN' => '&#122;&#322;',
571
		'PRB' => '&#x440;.',
572
		'PYG' => '&#8370;',
573
		'QAR' => '&#x631;.&#x642;',
574
		'RMB' => '&yen;',
575
		'RON' => 'lei',
576
		'RSD' => '&#x434;&#x438;&#x43d;.',
577
		'RUB' => '&#8381;',
578
		'RWF' => 'Fr',
579
		'SAR' => '&#x631;.&#x633;',
580
		'SBD' => '&#36;',
581
		'SCR' => '&#x20a8;',
582
		'SDG' => '&#x62c;.&#x633;.',
583
		'SEK' => '&#107;&#114;',
584
		'SGD' => '&#36;',
585
		'SHP' => '&pound;',
586
		'SLL' => 'Le',
587
		'SOS' => 'Sh',
588
		'SRD' => '&#36;',
589
		'SSP' => '&pound;',
590
		'STD' => 'Db',
591
		'SYP' => '&#x644;.&#x633;',
592
		'SZL' => 'L',
593
		'THB' => '&#3647;',
594
		'TJS' => '&#x405;&#x41c;',
595
		'TMT' => 'm',
596
		'TND' => '&#x62f;.&#x62a;',
597
		'TOP' => 'T&#36;',
598
		'TRY' => '&#8378;',
599
		'TTD' => '&#36;',
600
		'TWD' => '&#78;&#84;&#36;',
601
		'TZS' => 'Sh',
602
		'UAH' => '&#8372;',
603
		'UGX' => 'UGX',
604
		'USD' => '&#36;',
605
		'UYU' => '&#36;',
606
		'UZS' => 'UZS',
607
		'VEF' => 'Bs F',
608
		'VND' => '&#8363;',
609
		'VUV' => 'Vt',
610
		'WST' => 'T',
611
		'XAF' => 'Fr',
612
		'XCD' => '&#36;',
613
		'XOF' => 'Fr',
614
		'XPF' => 'Fr',
615
		'YER' => '&#xfdfc;',
616
		'ZAR' => '&#82;',
617
		'ZMW' => 'ZK',
618
	) );
619
620
	$currency_symbol = isset( $symbols[ $currency ] ) ? $symbols[ $currency ] : '';
621
622
	return apply_filters( 'woocommerce_currency_symbol', $currency_symbol, $currency );
623
}
624
625
/**
626
 * Send HTML emails from WooCommerce.
627
 *
628
 * @param mixed $to
629
 * @param mixed $subject
630
 * @param mixed $message
631
 * @param string $headers (default: "Content-Type: text/html\r\n")
632
 * @param string $attachments (default: "")
633
 */
634
function wc_mail( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = "" ) {
635
	$mailer = WC()->mailer();
636
637
	$mailer->send( $to, $subject, $message, $headers, $attachments );
638
}
639
640
/**
641
 * Get an image size.
642
 *
643
 * Variable is filtered by woocommerce_get_image_size_{image_size}.
644
 *
645
 * @param mixed $image_size
646
 * @return array
647
 */
648
function wc_get_image_size( $image_size ) {
649
	if ( is_array( $image_size ) ) {
650
		$width  = isset( $image_size[0] ) ? $image_size[0] : '300';
651
		$height = isset( $image_size[1] ) ? $image_size[1] : '300';
652
		$crop   = isset( $image_size[2] ) ? $image_size[2] : 1;
653
654
		$size = array(
655
			'width'  => $width,
656
			'height' => $height,
657
			'crop'   => $crop
658
		);
659
660
		$image_size = $width . '_' . $height;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $image_size. This often makes code more readable.
Loading history...
661
662
	} elseif ( in_array( $image_size, array( 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ) ) {
663
		$size           = get_option( $image_size . '_image_size', array() );
664
		$size['width']  = isset( $size['width'] ) ? $size['width'] : '300';
665
		$size['height'] = isset( $size['height'] ) ? $size['height'] : '300';
666
		$size['crop']   = isset( $size['crop'] ) ? $size['crop'] : 0;
667
668
	} else {
669
		$size = array(
670
			'width'  => '300',
671
			'height' => '300',
672
			'crop'   => 1
673
		);
674
	}
675
676
	return apply_filters( 'woocommerce_get_image_size_' . $image_size, $size );
677
}
678
679
/**
680
 * Queue some JavaScript code to be output in the footer.
681
 *
682
 * @param string $code
683
 */
684
function wc_enqueue_js( $code ) {
685
	global $wc_queued_js;
686
687
	if ( empty( $wc_queued_js ) ) {
688
		$wc_queued_js = '';
689
	}
690
691
	$wc_queued_js .= "\n" . $code . "\n";
692
}
693
694
/**
695
 * Output any queued javascript code in the footer.
696
 */
697
function wc_print_js() {
698
	global $wc_queued_js;
699
700
	if ( ! empty( $wc_queued_js ) ) {
701
		// Sanitize.
702
		$wc_queued_js = wp_check_invalid_utf8( $wc_queued_js );
703
		$wc_queued_js = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", $wc_queued_js );
704
		$wc_queued_js = str_replace( "\r", '', $wc_queued_js );
705
706
		$js = "<!-- WooCommerce JavaScript -->\n<script type=\"text/javascript\">\njQuery(function($) { $wc_queued_js });\n</script>\n";
707
708
		/**
709
		 * woocommerce_queued_js filter.
710
		 *
711
		 * @since 2.6.0
712
		 * @param string $js JavaScript code.
713
		 */
714
		echo apply_filters( 'woocommerce_queued_js', $js );
715
716
		unset( $wc_queued_js );
717
	}
718
}
719
720
/**
721
 * Set a cookie - wrapper for setcookie using WP constants.
722
 *
723
 * @param  string  $name   Name of the cookie being set.
724
 * @param  string  $value  Value of the cookie.
725
 * @param  integer $expire Expiry of the cookie.
726
 * @param  string  $secure Whether the cookie should be served only over https.
727
 */
728
function wc_setcookie( $name, $value, $expire = 0, $secure = false ) {
729
	if ( ! headers_sent() ) {
730
		setcookie( $name, $value, $expire, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, $secure );
731
	} elseif ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
732
		headers_sent( $file, $line );
733
		trigger_error( "{$name} cookie cannot be set - headers already sent by {$file} on line {$line}", E_USER_NOTICE );
734
	}
735
}
736
737
/**
738
 * Get the URL to the WooCommerce REST API.
739
 *
740
 * @since 2.1
741
 * @param string $path an endpoint to include in the URL.
742
 * @return string the URL.
743
 */
744
function get_woocommerce_api_url( $path ) {
745
	$version  = defined( 'WC_API_REQUEST_VERSION' ) ? WC_API_REQUEST_VERSION : substr( WC_API::VERSION, 0, 1 );
746
747
	$url = get_home_url( null, "wc-api/v{$version}/", is_ssl() ? 'https' : 'http' );
748
749
	if ( ! empty( $path ) && is_string( $path ) ) {
750
		$url .= ltrim( $path, '/' );
751
	}
752
753
	return $url;
754
}
755
756
/**
757
 * Get a log file path.
758
 *
759
 * @since 2.2
760
 * @param string $handle name.
761
 * @return string the log file path.
762
 */
763
function wc_get_log_file_path( $handle ) {
764
	return trailingslashit( WC_LOG_DIR ) . $handle . '-' . sanitize_file_name( wp_hash( $handle ) ) . '.log';
765
}
766
767
/**
768
 * Init for our rewrite rule fixes.
769
 */
770
function wc_fix_rewrite_rules_init() {
771
	$permalinks = get_option( 'woocommerce_permalinks' );
772
773
	if ( ! empty( $permalinks['use_verbose_page_rules'] ) ) {
774
		$GLOBALS['wp_rewrite']->use_verbose_page_rules = true;
775
	}
776
}
777
add_action( 'init', 'wc_fix_rewrite_rules_init' );
778
779
/**
780
 * Various rewrite rule fixes.
781
 *
782
 * @since 2.2
783
 * @param array $rules
784
 * @return array
785
 */
786
function wc_fix_rewrite_rules( $rules ) {
787
	global $wp_rewrite;
788
789
	$permalinks        = get_option( 'woocommerce_permalinks' );
790
	$product_permalink = empty( $permalinks['product_base'] ) ? _x( 'product', 'slug', 'woocommerce' ) : $permalinks['product_base'];
791
792
	// Fix the rewrite rules when the product permalink have %product_cat% flag.
793
	if ( preg_match( '`/(.+)(/%product_cat%)`' , $product_permalink, $matches ) ) {
794
		foreach ( $rules as $rule => $rewrite ) {
795
796
			if ( preg_match( '`^' . preg_quote( $matches[1], '`' ) . '/\(`', $rule ) && preg_match( '/^(index\.php\?product_cat)(?!(.*product))/', $rewrite ) ) {
797
				unset( $rules[ $rule ] );
798
			}
799
		}
800
	}
801
802
	// If the shop page is used as the base, we need to enable verbose rewrite rules or sub pages will 404.
803
	if ( ! empty( $permalinks['use_verbose_page_rules'] ) ) {
804
		$page_rewrite_rules = $wp_rewrite->page_rewrite_rules();
805
		$rules              = array_merge( $page_rewrite_rules, $rules );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $rules. This often makes code more readable.
Loading history...
806
	}
807
808
	return $rules;
809
}
810
add_filter( 'rewrite_rules_array', 'wc_fix_rewrite_rules' );
811
812
/**
813
 * Prevent product attachment links from breaking when using complex rewrite structures.
814
 *
815
 * @param  string $link
816
 * @param  id $post_id
817
 * @return string
818
 */
819
function wc_fix_product_attachment_link( $link, $post_id ) {
820
	global $wp_rewrite;
821
822
	$post = get_post( $post_id );
823
	if ( 'product' === get_post_type( $post->post_parent ) ) {
824
		$permalinks        = get_option( 'woocommerce_permalinks' );
825
		$product_permalink = empty( $permalinks['product_base'] ) ? _x( 'product', 'slug', 'woocommerce' ) : $permalinks['product_base'];
826
		if ( preg_match( '/\/(.+)(\/%product_cat%)$/' , $product_permalink, $matches ) ) {
827
			$link = home_url( '/?attachment_id=' . $post->ID );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $link. This often makes code more readable.
Loading history...
828
		}
829
	}
830
	return $link;
831
}
832
add_filter( 'attachment_link', 'wc_fix_product_attachment_link', 10, 2 );
833
834
/**
835
 * Protect downloads from ms-files.php in multisite.
836
 *
837
 * @param mixed $rewrite
838
 * @return string
839
 */
840
function wc_ms_protect_download_rewite_rules( $rewrite ) {
841
	if ( ! is_multisite() || 'redirect' == get_option( 'woocommerce_file_download_method' ) ) {
842
		return $rewrite;
843
	}
844
845
	$rule  = "\n# WooCommerce Rules - Protect Files from ms-files.php\n\n";
846
	$rule .= "<IfModule mod_rewrite.c>\n";
847
	$rule .= "RewriteEngine On\n";
848
	$rule .= "RewriteCond %{QUERY_STRING} file=woocommerce_uploads/ [NC]\n";
849
	$rule .= "RewriteRule /ms-files.php$ - [F]\n";
850
	$rule .= "</IfModule>\n\n";
851
852
	return $rule . $rewrite;
853
}
854
add_filter( 'mod_rewrite_rules', 'wc_ms_protect_download_rewite_rules' );
855
856
/**
857
 * WooCommerce Core Supported Themes.
858
 *
859
 * @since 2.2
860
 * @return string[]
861
 */
862
function wc_get_core_supported_themes() {
863
	return array( 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' );
864
}
865
866
/**
867
 * Wrapper function to execute the `woocommerce_deliver_webhook_async` cron.
868
 * hook, see WC_Webhook::process().
869
 *
870
 * @since 2.2
871
 * @param int $webhook_id webhook ID to deliver.
872
 * @param mixed $arg hook argument.
873
 */
874
function wc_deliver_webhook_async( $webhook_id, $arg ) {
875
876
	$webhook = new WC_Webhook( $webhook_id );
877
878
	$webhook->deliver( $arg );
879
}
880
add_action( 'woocommerce_deliver_webhook_async', 'wc_deliver_webhook_async', 10, 2 );
881
882
/**
883
 * Enables template debug mode.
884
 */
885
function wc_template_debug_mode() {
886
	if ( ! defined( 'WC_TEMPLATE_DEBUG_MODE' ) ) {
887
		$status_options = get_option( 'woocommerce_status_options', array() );
888
		if ( ! empty( $status_options['template_debug_mode'] ) && current_user_can( 'manage_options' ) ) {
889
			define( 'WC_TEMPLATE_DEBUG_MODE', true );
890
		} else {
891
			define( 'WC_TEMPLATE_DEBUG_MODE', false );
892
		}
893
	}
894
}
895
add_action( 'after_setup_theme', 'wc_template_debug_mode', 20 );
896
897
/**
898
 * Formats a string in the format COUNTRY:STATE into an array.
899
 *
900
 * @since 2.3.0
901
 * @param  string $country_string
902
 * @return array
903
 */
904
function wc_format_country_state_string( $country_string ) {
905
	if ( strstr( $country_string, ':' ) ) {
906
		list( $country, $state ) = explode( ':', $country_string );
907
	} else {
908
		$country = $country_string;
909
		$state   = '';
910
	}
911
	return array(
912
		'country' => $country,
913
		'state'   => $state
914
	);
915
}
916
917
/**
918
 * Get the store's base location.
919
 *
920
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
921
 * @since 2.3.0
922
 * @return array
923
 */
924
function wc_get_base_location() {
925
	$default = apply_filters( 'woocommerce_get_base_location', get_option( 'woocommerce_default_country' ) );
926
927
	return wc_format_country_state_string( $default );
928
}
929
930
/**
931
 * Get the customer's default location.
932
 *
933
 * Filtered, and set to base location or left blank. If cache-busting,
934
 * this should only be used when 'location' is set in the querystring.
935
 *
936
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
937
 * @todo deprecate woocommerce_customer_default_location and support an array filter only to cover all cases.
938
 * @since 2.3.0
939
 * @return array
940
 */
941
function wc_get_customer_default_location() {
942
	$location = array();
943
944
	switch ( get_option( 'woocommerce_default_customer_address' ) ) {
945
		case 'geolocation_ajax' :
946
		case 'geolocation' :
947
			// Exclude common bots from geolocation by user agent.
948
			$ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? strtolower( $_SERVER['HTTP_USER_AGENT'] ) : '';
949
950
			if ( ! strstr( $ua, 'bot' ) && ! strstr( $ua, 'spider' ) && ! strstr( $ua, 'crawl' ) ) {
951
				$location = WC_Geolocation::geolocate_ip( '', true, false );
952
			}
953
954
			// Base fallback.
955
			if ( empty( $location['country'] ) ) {
956
				$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
957
			}
958
		break;
959
		case 'base' :
960
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
961
		break;
962
		default :
963
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', '' ) );
964
		break;
965
	}
966
967
	return apply_filters( 'woocommerce_customer_default_location_array', $location );
968
}
969
970
// This function can be removed when WP 3.9.2 or greater is required.
971
if ( ! function_exists( 'hash_equals' ) ) :
972
	/**
973
	 * Compare two strings in constant time.
974
	 *
975
	 * This function was added in PHP 5.6.
976
	 * It can leak the length of a string.
977
	 *
978
	 * @since 3.9.2
979
	 *
980
	 * @param string $a Expected string.
981
	 * @param string $b Actual string.
982
	 * @return bool Whether strings are equal.
983
	 */
984
	function hash_equals( $a, $b ) {
985
		$a_length = strlen( $a );
986
		if ( $a_length !== strlen( $b ) ) {
987
			return false;
988
		}
989
		$result = 0;
990
991
		// Do not attempt to "optimize" this.
992
		for ( $i = 0; $i < $a_length; $i++ ) {
993
			$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
994
		}
995
996
		return $result === 0;
997
	}
998
endif;
999
1000
/**
1001
 * Generate a rand hash.
1002
 *
1003
 * @since  2.4.0
1004
 * @return string
1005
 */
1006
function wc_rand_hash() {
1007
	if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
1008
		return bin2hex( openssl_random_pseudo_bytes( 20 ) );
1009
	} else {
1010
		return sha1( wp_rand() );
1011
	}
1012
}
1013
1014
/**
1015
 * WC API - Hash.
1016
 *
1017
 * @since  2.4.0
1018
 * @param  string $data
1019
 * @return string
1020
 */
1021
function wc_api_hash( $data ) {
1022
	return hash_hmac( 'sha256', $data, 'wc-api' );
1023
}
1024
1025
/**
1026
 * Find all possible combinations of values from the input array and return in a logical order.
1027
 * @since 2.5.0
1028
 * @param array $input
1029
 * @return array
1030
 */
1031
function wc_array_cartesian( $input ) {
1032
	$input   = array_filter( $input );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $input. This often makes code more readable.
Loading history...
1033
	$results = array();
1034
	$indexes = array();
1035
	$index   = 0;
1036
1037
	// Generate indexes from keys and values so we have a logical sort order
1038
	foreach ( $input as $key => $values ) {
1039
		foreach ( $values as $value ) {
1040
			$indexes[ $key ][ $value ] = $index++;
1041
		}
1042
	}
1043
1044
	// Loop over the 2D array of indexes and generate all combinations
1045
	foreach ( $indexes as $key => $values ) {
1046
		// When result is empty, fill with the values of the first looped array
1047
		if ( empty( $results ) ) {
1048
			foreach ( $values as $value ) {
1049
				$results[] = array( $key => $value );
1050
			}
1051
1052
		// Second and subsequent input sub-array merging.
1053
		} else {
1054
			foreach ( $results as $result_key => $result ) {
1055
				foreach ( $values as $value ) {
1056
					// If the key is not set, we can set it
1057
					if ( ! isset( $results[ $result_key ][ $key ] ) ) {
1058
						$results[ $result_key ][ $key ] = $value;
1059
					// If the key is set, we can add a new combination to the results array
1060
					} else {
1061
						$new_combination         = $results[ $result_key ];
1062
						$new_combination[ $key ] = $value;
1063
						$results[]               = $new_combination;
1064
					}
1065
				}
1066
			}
1067
		}
1068
	}
1069
1070
	// Sort the indexes
1071
	arsort( $results );
1072
1073
	// Convert indexes back to values
1074
	foreach ( $results as $result_key => $result ) {
1075
		$converted_values = array();
1076
1077
		// Sort the values
1078
		arsort( $results[ $result_key ] );
1079
1080
		// Convert the values
1081
		foreach ( $results[ $result_key ] as $key => $value ) {
1082
			$converted_values[ $key ] = array_search( $value, $indexes[ $key ] );
1083
		}
1084
1085
		$results[ $result_key ] = $converted_values;
1086
	}
1087
1088
	return $results;
1089
}
1090
1091
/**
1092
 * Run a MySQL transaction query, if supported.
1093
 * @param string $type start (default), commit, rollback
1094
 * @since 2.5.0
1095
 */
1096
function wc_transaction_query( $type = 'start' ) {
1097
	global $wpdb;
1098
1099
	$wpdb->hide_errors();
1100
1101
	if ( ! defined( 'WC_USE_TRANSACTIONS' ) ) {
1102
		define( 'WC_USE_TRANSACTIONS', true );
1103
	}
1104
1105
	if ( WC_USE_TRANSACTIONS ) {
1106
		switch ( $type ) {
1107
			case 'commit' :
1108
				$wpdb->query( 'COMMIT' );
1109
				break;
1110
			case 'rollback' :
1111
				$wpdb->query( 'ROLLBACK' );
1112
				break;
1113
			default :
1114
				$wpdb->query( 'START TRANSACTION' );
1115
			break;
1116
		}
1117
	}
1118
}
1119
1120
/**
1121
 * Gets the url to the cart page.
1122
 *
1123
 * @since  2.5.0
1124
 *
1125
 * @return string Url to cart page
1126
 */
1127
function wc_get_cart_url() {
1128
	return apply_filters( 'woocommerce_get_cart_url', wc_get_page_permalink( 'cart' ) );
1129
}
1130
1131
/**
1132
 * Gets the url to the checkout page.
1133
 *
1134
 * @since  2.5.0
1135
 *
1136
 * @return string Url to checkout page
1137
 */
1138
function wc_get_checkout_url() {
1139
	$checkout_url = wc_get_page_permalink( 'checkout' );
1140 View Code Duplication
	if ( $checkout_url ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1141
		// Force SSL if needed
1142
		if ( is_ssl() || 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) ) {
1143
			$checkout_url = str_replace( 'http:', 'https:', $checkout_url );
1144
		}
1145
	}
1146
1147
	return apply_filters( 'woocommerce_get_checkout_url', $checkout_url );
1148
}
1149
1150
/**
1151
 * Register a shipping method.
1152
 *
1153
 * @since 1.5.7
1154
 * @param string|object $shipping_method class name (string) or a class object.
1155
 */
1156
function woocommerce_register_shipping_method( $shipping_method ) {
1157
	WC()->shipping->register_shipping_method( $shipping_method );
1158
}
1159
1160
if ( ! function_exists( 'wc_get_shipping_zone' ) ) {
1161
	/**
1162
	 * Get the shipping zone matching a given package from the cart.
1163
	 *
1164
	 * @since  2.6.0
1165
	 * @uses   WC_Shipping_Zones::get_zone_matching_package
1166
	 * @param  array $package
1167
	 * @return WC_Shipping_Zone
1168
	 */
1169
	function wc_get_shipping_zone( $package ) {
1170
		return WC_Shipping_Zones::get_zone_matching_package( $package );
0 ignored issues
show
Documentation introduced by
$package is of type array, but the function expects a object.

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...
1171
	}
1172
}
1173
1174
/**
1175
 * Get a nice name for credit card providers.
1176
 *
1177
 * @since  2.6.0
1178
 * @param  string $type Provider Slug/Type
1179
 * @return string
1180
 */
1181
function wc_get_credit_card_type_label( $type ) {
1182
	// Normalize
1183
	$type = strtolower( $type );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $type. This often makes code more readable.
Loading history...
1184
	$type = str_replace( '-', ' ', $type );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $type. This often makes code more readable.
Loading history...
1185
	$type = str_replace( '_', ' ', $type );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $type. This often makes code more readable.
Loading history...
1186
1187
	$labels = apply_filters( 'wocommerce_credit_card_type_labels', array(
1188
		'mastercard'       => __( 'MasterCard', 'woocommerce' ),
1189
		'visa'             => __( 'Visa', 'woocommerce' ),
1190
		'discover'         => __( 'Discover', 'woocommerce' ),
1191
		'american express' => __( 'American Express', 'woocommerce' ),
1192
		'diners'           => __( 'Diners', 'woocommerce' ),
1193
		'jcb'              => __( 'JCB', 'woocommerce' ),
1194
	) );
1195
1196
	return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
1197
}
1198
1199
/**
1200
 * Outputs a "back" link so admin screens can easily jump back a page.
1201
 *
1202
 * @param string $label Title of the page to return to.
1203
 * @param string $url   URL of the page to return to.
1204
 */
1205
function wc_back_link( $label, $url ) {
1206
	echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" title="' . esc_attr( $label ) . '">&#x2934;</a></small>';
1207
}
1208
1209
/**
1210
 * Display a WooCommerce help tip.
1211
 *
1212
 * @since  2.5.0
1213
 *
1214
 * @param  string $tip        Help tip text
1215
 * @param  bool   $allow_html Allow sanitized HTML if true or escape
1216
 * @return string
1217
 */
1218
function wc_help_tip( $tip, $allow_html = false ) {
1219
	if ( $allow_html ) {
1220
		$tip = wc_sanitize_tooltip( $tip );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $tip. This often makes code more readable.
Loading history...
1221
	} else {
1222
		$tip = esc_attr( $tip );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $tip. This often makes code more readable.
Loading history...
1223
	}
1224
1225
	return '<span class="woocommerce-help-tip" data-tip="' . $tip . '"></span>';
1226
}
1227
1228
/**
1229
 * Return a list of potential postcodes for wildcard searching.
1230
 * @since 2.6.0
1231
 * @param  string $postcode
1232
 * @param  string $country to format postcode for matching.
1233
 * @return string[]
1234
 */
1235
function wc_get_wildcard_postcodes( $postcode, $country = '' ) {
1236
	$postcodes       = array( $postcode );
1237
	$postcode        = wc_format_postcode( $postcode, $country );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $postcode. This often makes code more readable.
Loading history...
1238
	$postcodes[]     = $postcode;
1239
	$postcode_length = strlen( $postcode );
1240
1241
	for ( $i = 0; $i < $postcode_length; $i ++ ) {
1242
		$postcodes[] = substr( $postcode, 0, ( $i + 1 ) * -1 ) . '*';
1243
	}
1244
1245
	return $postcodes;
1246
}
1247
1248
/**
1249
 * Used by shipping zones and taxes to compare a given $postcode to stored
1250
 * postcodes to find matches for numerical ranges, and wildcards.
1251
 * @since 2.6.0
1252
 * @param string $postcode Postcode you want to match against stored postcodes
1253
 * @param array  $objects Array of postcode objects from Database
1254
 * @param string $object_id_key DB column name for the ID.
1255
 * @param string $object_compare_key DB column name for the value.
1256
 * @param string $country Country from which this postcode belongs. Allows for formatting.
1257
 * @return array Array of matching object ID and matching values.
1258
 */
1259
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key, $country = '' ) {
1260
	$postcode           = wc_normalize_postcode( $postcode );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $postcode. This often makes code more readable.
Loading history...
1261
	$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode, $country ) );
1262
	$matches            = array();
1263
1264
	foreach ( $objects as $object ) {
1265
		$object_id       = $object->$object_id_key;
1266
		$compare_against = $object->$object_compare_key;
1267
1268
		// Handle postcodes containing ranges.
1269
		if ( strstr( $compare_against, '...' ) ) {
1270
			$range = array_map( 'trim', explode( '...', $compare_against ) );
1271
1272
			if ( 2 !== sizeof( $range ) ) {
1273
				continue;
1274
			}
1275
1276
			list( $min, $max ) = $range;
1277
1278
			// If the postcode is non-numeric, make it numeric.
1279
			if ( ! is_numeric( $min ) || ! is_numeric( $max ) ) {
1280
				$compare = wc_make_numeric_postcode( $postcode );
1281
				$min     = str_pad( wc_make_numeric_postcode( $min ), strlen( $compare ), '0' );
1282
				$max     = str_pad( wc_make_numeric_postcode( $max ), strlen( $compare ), '0' );
1283
			} else {
1284
				$compare = $postcode;
1285
			}
1286
1287
			if ( $compare >= $min && $compare <= $max ) {
1288
				$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1289
				$matches[ $object_id ][] = $compare_against;
1290
			}
1291
1292
		// Wildcard and standard comparison.
1293
		} elseif ( in_array( $compare_against, $wildcard_postcodes ) ) {
1294
			$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1295
			$matches[ $object_id ][] = $compare_against;
1296
		}
1297
	}
1298
1299
	return $matches;
1300
}
1301
1302
/**
1303
 * Gets number of shipping methods currently enabled. Used to identify if
1304
 * shipping is configured.
1305
 *
1306
 * @since  2.6.0
1307
 * @param  bool $include_legacy Count legacy shipping methods too.
1308
 * @return int
1309
 */
1310
function wc_get_shipping_method_count( $include_legacy = false ) {
1311
	global $wpdb;
1312
1313
	$transient_name = 'wc_shipping_method_count_' . ( $include_legacy ? 1 : 0 ) . '_' . WC_Cache_Helper::get_transient_version( 'shipping' );
1314
	$method_count   = get_transient( $transient_name );
1315
1316
	if ( false === $method_count ) {
1317
		$method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) );
1318
1319
		if ( $include_legacy ) {
1320
			// Count activated methods that don't support shipping zones.
1321
			$methods = WC()->shipping->get_shipping_methods();
1322
1323
			foreach ( $methods as $method ) {
1324
				if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) {
1325
					$method_count++;
1326
				}
1327
			}
1328
		}
1329
1330
		set_transient( $transient_name, $method_count, DAY_IN_SECONDS * 30 );
1331
	}
1332
1333
	return absint( $method_count );
1334
}
1335
1336
/**
1337
 * Wrapper for set_time_limit to see if it is enabled.
1338
 * @since 2.6.0
1339
 */
1340
function wc_set_time_limit( $limit = 0 ) {
1341
	if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
1342
		@set_time_limit( $limit );
1343
	}
1344
}
1345
1346
/**
1347
 * Used to sort products attributes with uasort.
1348
 * @since 2.6.0
1349
 */
1350
function wc_product_attribute_uasort_comparison( $a, $b ) {
1351
	if ( $a['position'] == $b['position'] ) {
1352
		return 0;
1353
	}
1354
	return ( $a['position'] < $b['position'] ) ? -1 : 1;
1355
}
1356
1357
/**
1358
 * Get rounding precision.
1359
 *
1360
 * @since 2.6.3
1361
 * @return int
1362
 */
1363
function wc_get_rounding_precision() {
1364
	if ( defined( 'WC_ROUNDING_PRECISION' ) ) {
1365
		return absint( WC_ROUNDING_PRECISION );
1366
	}
1367
1368
	return wc_get_price_decimals() + 2;
1369
}
1370