Completed
Push — master ( 874437...cfe28c )
by Mike
14:14
created

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

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
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 );
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();
238
	}
239
240
	if ( ! $default_path ) {
241
		$default_path = WC()->plugin_path() . '/templates/';
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();
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;
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
 * Recursively get page children.
769
 * @param  int $page_id
770
 * @return int[]
771
 */
772
function wc_get_page_children( $page_id ) {
773
	$page_ids = get_posts( array(
774
		'post_parent' => $page_id,
775
		'post_type'   => 'page',
776
		'numberposts' => -1,
777
		'post_status' => 'any',
778
		'fields'      => 'ids',
779
	) );
780
781
	if ( ! empty( $page_ids ) ) {
782
		foreach ( $page_ids as $page_id ) {
783
			$page_ids = array_merge( $page_ids, wc_get_page_children( $page_id ) );
784
		}
785
	}
786
787
	return $page_ids;
788
}
789
790
/**
791
 * Flushes rewrite rules when the shop page (or it's children) gets saved.
792
 */
793
function flush_rewrite_rules_on_shop_page_save( $post_id ) {
794
	$shop_page_id = wc_get_page_id( 'shop' );
795
	if ( $shop_page_id === $post_id || in_array( $post_id, wc_get_page_children( $shop_page_id ) ) ) {
796
		flush_rewrite_rules();
797
	}
798
}
799
add_action( 'save_post', 'flush_rewrite_rules_on_shop_page_save' );
800
801
/**
802
 * Various rewrite rule fixes.
803
 *
804
 * @since 2.2
805
 * @param array $rules
806
 * @return array
807
 */
808
function wc_fix_rewrite_rules( $rules ) {
809
	global $wp_rewrite;
810
811
	$permalinks        = get_option( 'woocommerce_permalinks' );
812
	$product_permalink = empty( $permalinks['product_base'] ) ? _x( 'product', 'slug', 'woocommerce' ) : $permalinks['product_base'];
813
814
	// Fix the rewrite rules when the product permalink have %product_cat% flag.
815
	if ( preg_match( '`/(.+)(/%product_cat%)`' , $product_permalink, $matches ) ) {
816
		foreach ( $rules as $rule => $rewrite ) {
817
			if ( preg_match( '`^' . preg_quote( $matches[1], '`' ) . '/\(`', $rule ) && preg_match( '/^(index\.php\?product_cat)(?!(.*product))/', $rewrite ) ) {
818
				unset( $rules[ $rule ] );
819
			}
820
		}
821
	}
822
823
	// If the shop page is used as the base, we need to handle shop page subpages to avoid 404s.
824
	if ( ! empty( $permalinks['use_verbose_page_rules'] ) && ( $shop_page_id = wc_get_page_id( 'shop' ) ) ) {
825
		$page_rewrite_rules = array();
826
		$subpages           = wc_get_page_children( $shop_page_id );
827
828
		// Subpage rules
829
		foreach ( $subpages as $subpage ) {
830
			$uri = get_page_uri( $subpage );
831
			$page_rewrite_rules[ $uri . '/?$' ] = 'index.php?pagename=' . $uri;
832
			$wp_generated_rewrite_rules         = $wp_rewrite->generate_rewrite_rules( $uri, EP_PAGES, true, true, false, false );
833
			foreach ( $wp_generated_rewrite_rules as $key => $value ) {
834
				$wp_generated_rewrite_rules[ $key ] = $value . '&pagename=' . $uri;
835
			}
836
			$page_rewrite_rules = array_merge( $page_rewrite_rules, $wp_generated_rewrite_rules );
837
		}
838
839
		// Merge with rules
840
		$rules = array_merge( $page_rewrite_rules, $rules );
841
	}
842
843
	return $rules;
844
}
845
add_filter( 'rewrite_rules_array', 'wc_fix_rewrite_rules' );
846
847
/**
848
 * Prevent product attachment links from breaking when using complex rewrite structures.
849
 *
850
 * @param  string $link
851
 * @param  id $post_id
852
 * @return string
853
 */
854
function wc_fix_product_attachment_link( $link, $post_id ) {
855
	global $wp_rewrite;
856
857
	$post = get_post( $post_id );
858
	if ( 'product' === get_post_type( $post->post_parent ) ) {
859
		$permalinks        = get_option( 'woocommerce_permalinks' );
860
		$product_permalink = empty( $permalinks['product_base'] ) ? _x( 'product', 'slug', 'woocommerce' ) : $permalinks['product_base'];
861
		if ( preg_match( '/\/(.+)(\/%product_cat%)$/' , $product_permalink, $matches ) ) {
862
			$link = home_url( '/?attachment_id=' . $post->ID );
863
		}
864
	}
865
	return $link;
866
}
867
add_filter( 'attachment_link', 'wc_fix_product_attachment_link', 10, 2 );
868
869
/**
870
 * Protect downloads from ms-files.php in multisite.
871
 *
872
 * @param mixed $rewrite
873
 * @return string
874
 */
875
function wc_ms_protect_download_rewite_rules( $rewrite ) {
876
	if ( ! is_multisite() || 'redirect' == get_option( 'woocommerce_file_download_method' ) ) {
877
		return $rewrite;
878
	}
879
880
	$rule  = "\n# WooCommerce Rules - Protect Files from ms-files.php\n\n";
881
	$rule .= "<IfModule mod_rewrite.c>\n";
882
	$rule .= "RewriteEngine On\n";
883
	$rule .= "RewriteCond %{QUERY_STRING} file=woocommerce_uploads/ [NC]\n";
884
	$rule .= "RewriteRule /ms-files.php$ - [F]\n";
885
	$rule .= "</IfModule>\n\n";
886
887
	return $rule . $rewrite;
888
}
889
add_filter( 'mod_rewrite_rules', 'wc_ms_protect_download_rewite_rules' );
890
891
/**
892
 * WooCommerce Core Supported Themes.
893
 *
894
 * @since 2.2
895
 * @return string[]
896
 */
897
function wc_get_core_supported_themes() {
898
	return array( 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' );
899
}
900
901
/**
902
 * Wrapper function to execute the `woocommerce_deliver_webhook_async` cron.
903
 * hook, see WC_Webhook::process().
904
 *
905
 * @since 2.2
906
 * @param int $webhook_id webhook ID to deliver.
907
 * @param mixed $arg hook argument.
908
 */
909
function wc_deliver_webhook_async( $webhook_id, $arg ) {
910
911
	$webhook = new WC_Webhook( $webhook_id );
912
913
	$webhook->deliver( $arg );
914
}
915
add_action( 'woocommerce_deliver_webhook_async', 'wc_deliver_webhook_async', 10, 2 );
916
917
/**
918
 * Enables template debug mode.
919
 */
920
function wc_template_debug_mode() {
921
	if ( ! defined( 'WC_TEMPLATE_DEBUG_MODE' ) ) {
922
		$status_options = get_option( 'woocommerce_status_options', array() );
923
		if ( ! empty( $status_options['template_debug_mode'] ) && current_user_can( 'manage_options' ) ) {
924
			define( 'WC_TEMPLATE_DEBUG_MODE', true );
925
		} else {
926
			define( 'WC_TEMPLATE_DEBUG_MODE', false );
927
		}
928
	}
929
}
930
add_action( 'after_setup_theme', 'wc_template_debug_mode', 20 );
931
932
/**
933
 * Formats a string in the format COUNTRY:STATE into an array.
934
 *
935
 * @since 2.3.0
936
 * @param  string $country_string
937
 * @return array
938
 */
939
function wc_format_country_state_string( $country_string ) {
940
	if ( strstr( $country_string, ':' ) ) {
941
		list( $country, $state ) = explode( ':', $country_string );
942
	} else {
943
		$country = $country_string;
944
		$state   = '';
945
	}
946
	return array(
947
		'country' => $country,
948
		'state'   => $state
949
	);
950
}
951
952
/**
953
 * Get the store's base location.
954
 *
955
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
956
 * @since 2.3.0
957
 * @return array
958
 */
959
function wc_get_base_location() {
960
	$default = apply_filters( 'woocommerce_get_base_location', get_option( 'woocommerce_default_country' ) );
961
962
	return wc_format_country_state_string( $default );
963
}
964
965
/**
966
 * Get the customer's default location.
967
 *
968
 * Filtered, and set to base location or left blank. If cache-busting,
969
 * this should only be used when 'location' is set in the querystring.
970
 *
971
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
972
 * @todo deprecate woocommerce_customer_default_location and support an array filter only to cover all cases.
973
 * @since 2.3.0
974
 * @return array
975
 */
976
function wc_get_customer_default_location() {
977
	$location = array();
978
979
	switch ( get_option( 'woocommerce_default_customer_address' ) ) {
980
		case 'geolocation_ajax' :
981
		case 'geolocation' :
982
			// Exclude common bots from geolocation by user agent.
983
			$ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? strtolower( $_SERVER['HTTP_USER_AGENT'] ) : '';
984
985
			if ( ! strstr( $ua, 'bot' ) && ! strstr( $ua, 'spider' ) && ! strstr( $ua, 'crawl' ) ) {
986
				$location = WC_Geolocation::geolocate_ip( '', true, false );
987
			}
988
989
			// Base fallback.
990
			if ( empty( $location['country'] ) ) {
991
				$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
992
			}
993
		break;
994
		case 'base' :
995
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
996
		break;
997
		default :
998
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', '' ) );
999
		break;
1000
	}
1001
1002
	return apply_filters( 'woocommerce_customer_default_location_array', $location );
1003
}
1004
1005
// This function can be removed when WP 3.9.2 or greater is required.
1006
if ( ! function_exists( 'hash_equals' ) ) :
1007
	/**
1008
	 * Compare two strings in constant time.
1009
	 *
1010
	 * This function was added in PHP 5.6.
1011
	 * It can leak the length of a string.
1012
	 *
1013
	 * @since 3.9.2
1014
	 *
1015
	 * @param string $a Expected string.
1016
	 * @param string $b Actual string.
1017
	 * @return bool Whether strings are equal.
1018
	 */
1019
	function hash_equals( $a, $b ) {
1020
		$a_length = strlen( $a );
1021
		if ( $a_length !== strlen( $b ) ) {
1022
			return false;
1023
		}
1024
		$result = 0;
1025
1026
		// Do not attempt to "optimize" this.
1027
		for ( $i = 0; $i < $a_length; $i++ ) {
1028
			$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
1029
		}
1030
1031
		return $result === 0;
1032
	}
1033
endif;
1034
1035
/**
1036
 * Generate a rand hash.
1037
 *
1038
 * @since  2.4.0
1039
 * @return string
1040
 */
1041
function wc_rand_hash() {
1042
	if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
1043
		return bin2hex( openssl_random_pseudo_bytes( 20 ) );
1044
	} else {
1045
		return sha1( wp_rand() );
1046
	}
1047
}
1048
1049
/**
1050
 * WC API - Hash.
1051
 *
1052
 * @since  2.4.0
1053
 * @param  string $data
1054
 * @return string
1055
 */
1056
function wc_api_hash( $data ) {
1057
	return hash_hmac( 'sha256', $data, 'wc-api' );
1058
}
1059
1060
/**
1061
 * Find all possible combinations of values from the input array and return in a logical order.
1062
 * @since 2.5.0
1063
 * @param array $input
1064
 * @return array
1065
 */
1066
function wc_array_cartesian( $input ) {
1067
	$input   = array_filter( $input );
1068
	$results = array();
1069
	$indexes = array();
1070
	$index   = 0;
1071
1072
	// Generate indexes from keys and values so we have a logical sort order
1073
	foreach ( $input as $key => $values ) {
1074
		foreach ( $values as $value ) {
1075
			$indexes[ $key ][ $value ] = $index++;
1076
		}
1077
	}
1078
1079
	// Loop over the 2D array of indexes and generate all combinations
1080
	foreach ( $indexes as $key => $values ) {
1081
		// When result is empty, fill with the values of the first looped array
1082
		if ( empty( $results ) ) {
1083
			foreach ( $values as $value ) {
1084
				$results[] = array( $key => $value );
1085
			}
1086
1087
		// Second and subsequent input sub-array merging.
1088
		} else {
1089
			foreach ( $results as $result_key => $result ) {
1090
				foreach ( $values as $value ) {
1091
					// If the key is not set, we can set it
1092
					if ( ! isset( $results[ $result_key ][ $key ] ) ) {
1093
						$results[ $result_key ][ $key ] = $value;
1094
					// If the key is set, we can add a new combination to the results array
1095
					} else {
1096
						$new_combination         = $results[ $result_key ];
1097
						$new_combination[ $key ] = $value;
1098
						$results[]               = $new_combination;
1099
					}
1100
				}
1101
			}
1102
		}
1103
	}
1104
1105
	// Sort the indexes
1106
	arsort( $results );
1107
1108
	// Convert indexes back to values
1109
	foreach ( $results as $result_key => $result ) {
1110
		$converted_values = array();
1111
1112
		// Sort the values
1113
		arsort( $results[ $result_key ] );
1114
1115
		// Convert the values
1116
		foreach ( $results[ $result_key ] as $key => $value ) {
1117
			$converted_values[ $key ] = array_search( $value, $indexes[ $key ] );
1118
		}
1119
1120
		$results[ $result_key ] = $converted_values;
1121
	}
1122
1123
	return $results;
1124
}
1125
1126
/**
1127
 * Run a MySQL transaction query, if supported.
1128
 * @param string $type start (default), commit, rollback
1129
 * @since 2.5.0
1130
 */
1131
function wc_transaction_query( $type = 'start' ) {
1132
	global $wpdb;
1133
1134
	$wpdb->hide_errors();
1135
1136
	if ( ! defined( 'WC_USE_TRANSACTIONS' ) ) {
1137
		define( 'WC_USE_TRANSACTIONS', true );
1138
	}
1139
1140
	if ( WC_USE_TRANSACTIONS ) {
1141
		switch ( $type ) {
1142
			case 'commit' :
1143
				$wpdb->query( 'COMMIT' );
1144
				break;
1145
			case 'rollback' :
1146
				$wpdb->query( 'ROLLBACK' );
1147
				break;
1148
			default :
1149
				$wpdb->query( 'START TRANSACTION' );
1150
			break;
1151
		}
1152
	}
1153
}
1154
1155
/**
1156
 * Gets the url to the cart page.
1157
 *
1158
 * @since  2.5.0
1159
 *
1160
 * @return string Url to cart page
1161
 */
1162
function wc_get_cart_url() {
1163
	return apply_filters( 'woocommerce_get_cart_url', wc_get_page_permalink( 'cart' ) );
1164
}
1165
1166
/**
1167
 * Gets the url to the checkout page.
1168
 *
1169
 * @since  2.5.0
1170
 *
1171
 * @return string Url to checkout page
1172
 */
1173
function wc_get_checkout_url() {
1174
	$checkout_url = wc_get_page_permalink( 'checkout' );
1175 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...
1176
		// Force SSL if needed
1177
		if ( is_ssl() || 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) ) {
1178
			$checkout_url = str_replace( 'http:', 'https:', $checkout_url );
1179
		}
1180
	}
1181
1182
	return apply_filters( 'woocommerce_get_checkout_url', $checkout_url );
1183
}
1184
1185
/**
1186
 * Register a shipping method.
1187
 *
1188
 * @since 1.5.7
1189
 * @param string|object $shipping_method class name (string) or a class object.
1190
 */
1191
function woocommerce_register_shipping_method( $shipping_method ) {
1192
	WC()->shipping->register_shipping_method( $shipping_method );
1193
}
1194
1195
if ( ! function_exists( 'wc_get_shipping_zone' ) ) {
1196
	/**
1197
	 * Get the shipping zone matching a given package from the cart.
1198
	 *
1199
	 * @since  2.6.0
1200
	 * @uses   WC_Shipping_Zones::get_zone_matching_package
1201
	 * @param  array $package
1202
	 * @return WC_Shipping_Zone
1203
	 */
1204
	function wc_get_shipping_zone( $package ) {
1205
		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...
1206
	}
1207
}
1208
1209
/**
1210
 * Get a nice name for credit card providers.
1211
 *
1212
 * @since  2.6.0
1213
 * @param  string $type Provider Slug/Type
1214
 * @return string
1215
 */
1216
function wc_get_credit_card_type_label( $type ) {
1217
	// Normalize
1218
	$type = strtolower( $type );
1219
	$type = str_replace( '-', ' ', $type );
1220
	$type = str_replace( '_', ' ', $type );
1221
1222
	$labels = apply_filters( 'wocommerce_credit_card_type_labels', array(
1223
		'mastercard'       => __( 'MasterCard', 'woocommerce' ),
1224
		'visa'             => __( 'Visa', 'woocommerce' ),
1225
		'discover'         => __( 'Discover', 'woocommerce' ),
1226
		'american express' => __( 'American Express', 'woocommerce' ),
1227
		'diners'           => __( 'Diners', 'woocommerce' ),
1228
		'jcb'              => __( 'JCB', 'woocommerce' ),
1229
	) );
1230
1231
	return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
1232
}
1233
1234
/**
1235
 * Outputs a "back" link so admin screens can easily jump back a page.
1236
 *
1237
 * @param string $label Title of the page to return to.
1238
 * @param string $url   URL of the page to return to.
1239
 */
1240
function wc_back_link( $label, $url ) {
1241
	echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" title="' . esc_attr( $label ) . '">&#x2934;</a></small>';
1242
}
1243
1244
/**
1245
 * Display a WooCommerce help tip.
1246
 *
1247
 * @since  2.5.0
1248
 *
1249
 * @param  string $tip        Help tip text
1250
 * @param  bool   $allow_html Allow sanitized HTML if true or escape
1251
 * @return string
1252
 */
1253
function wc_help_tip( $tip, $allow_html = false ) {
1254
	if ( $allow_html ) {
1255
		$tip = wc_sanitize_tooltip( $tip );
1256
	} else {
1257
		$tip = esc_attr( $tip );
1258
	}
1259
1260
	return '<span class="woocommerce-help-tip" data-tip="' . $tip . '"></span>';
1261
}
1262
1263
/**
1264
 * Return a list of potential postcodes for wildcard searching.
1265
 * @since 2.6.0
1266
 * @param  string $postcode
1267
 * @param  string $country to format postcode for matching.
1268
 * @return string[]
1269
 */
1270
function wc_get_wildcard_postcodes( $postcode, $country = '' ) {
1271
	$postcodes       = array( $postcode );
1272
	$postcode        = wc_format_postcode( $postcode, $country );
1273
	$postcodes[]     = $postcode;
1274
	$postcode_length = strlen( $postcode );
1275
1276
	for ( $i = 0; $i < $postcode_length; $i ++ ) {
1277
		$postcodes[] = substr( $postcode, 0, ( $i + 1 ) * -1 ) . '*';
1278
	}
1279
1280
	return $postcodes;
1281
}
1282
1283
/**
1284
 * Used by shipping zones and taxes to compare a given $postcode to stored
1285
 * postcodes to find matches for numerical ranges, and wildcards.
1286
 * @since 2.6.0
1287
 * @param string $postcode Postcode you want to match against stored postcodes
1288
 * @param array  $objects Array of postcode objects from Database
1289
 * @param string $object_id_key DB column name for the ID.
1290
 * @param string $object_compare_key DB column name for the value.
1291
 * @param string $country Country from which this postcode belongs. Allows for formatting.
1292
 * @return array Array of matching object ID and matching values.
1293
 */
1294
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key, $country = '' ) {
1295
	$postcode           = wc_normalize_postcode( $postcode );
1296
	$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode, $country ) );
1297
	$matches            = array();
1298
1299
	foreach ( $objects as $object ) {
1300
		$object_id       = $object->$object_id_key;
1301
		$compare_against = $object->$object_compare_key;
1302
1303
		// Handle postcodes containing ranges.
1304
		if ( strstr( $compare_against, '...' ) ) {
1305
			$range = array_map( 'trim', explode( '...', $compare_against ) );
1306
1307
			if ( 2 !== sizeof( $range ) ) {
1308
				continue;
1309
			}
1310
1311
			list( $min, $max ) = $range;
1312
1313
			// If the postcode is non-numeric, make it numeric.
1314
			if ( ! is_numeric( $min ) || ! is_numeric( $max ) ) {
1315
				$compare = wc_make_numeric_postcode( $postcode );
1316
				$min     = str_pad( wc_make_numeric_postcode( $min ), strlen( $compare ), '0' );
1317
				$max     = str_pad( wc_make_numeric_postcode( $max ), strlen( $compare ), '0' );
1318
			} else {
1319
				$compare = $postcode;
1320
			}
1321
1322
			if ( $compare >= $min && $compare <= $max ) {
1323
				$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1324
				$matches[ $object_id ][] = $compare_against;
1325
			}
1326
1327
		// Wildcard and standard comparison.
1328
		} elseif ( in_array( $compare_against, $wildcard_postcodes ) ) {
1329
			$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1330
			$matches[ $object_id ][] = $compare_against;
1331
		}
1332
	}
1333
1334
	return $matches;
1335
}
1336
1337
/**
1338
 * Gets number of shipping methods currently enabled. Used to identify if
1339
 * shipping is configured.
1340
 *
1341
 * @since  2.6.0
1342
 * @param  bool $include_legacy Count legacy shipping methods too.
1343
 * @return int
1344
 */
1345
function wc_get_shipping_method_count( $include_legacy = false ) {
1346
	global $wpdb;
1347
1348
	$transient_name = 'wc_shipping_method_count_' . ( $include_legacy ? 1 : 0 ) . '_' . WC_Cache_Helper::get_transient_version( 'shipping' );
1349
	$method_count   = get_transient( $transient_name );
1350
1351
	if ( false === $method_count ) {
1352
		$method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) );
1353
1354
		if ( $include_legacy ) {
1355
			// Count activated methods that don't support shipping zones.
1356
			$methods = WC()->shipping->get_shipping_methods();
1357
1358
			foreach ( $methods as $method ) {
1359
				if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) {
1360
					$method_count++;
1361
				}
1362
			}
1363
		}
1364
1365
		set_transient( $transient_name, $method_count, DAY_IN_SECONDS * 30 );
1366
	}
1367
1368
	return absint( $method_count );
1369
}
1370
1371
/**
1372
 * Wrapper for set_time_limit to see if it is enabled.
1373
 * @since 2.6.0
1374
 */
1375
function wc_set_time_limit( $limit = 0 ) {
1376
	if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
1377
		@set_time_limit( $limit );
1378
	}
1379
}
1380
1381
/**
1382
 * Used to sort products attributes with uasort.
1383
 * @since 2.6.0
1384
 */
1385
function wc_product_attribute_uasort_comparison( $a, $b ) {
1386
	if ( $a['position'] == $b['position'] ) {
1387
		return 0;
1388
	}
1389
	return ( $a['position'] < $b['position'] ) ? -1 : 1;
1390
}
1391
1392
/**
1393
 * Used to sort shipping zone methods with uasort.
1394
 * @since 2.7.0
1395
 */
1396
function wc_shipping_zone_method_order_uasort_comparison( $a, $b ) {
1397
	if ( $a->method_order === $b->method_order ) {
1398
		return 0;
1399
	}
1400
	return ( $a->method_order < $b->method_order ) ? -1 : 1;
1401
}
1402
1403
1404
/**
1405
 * Get rounding precision for internal WC calculations.
1406
 * Will increase the precision of wc_get_price_decimals by 2 decimals, unless WC_ROUNDING_PRECISION is set to a higher number.
1407
 *
1408
 * @since 2.6.3
1409
 * @return int
1410
 */
1411
function wc_get_rounding_precision() {
1412
	$precision = wc_get_price_decimals() + 2;
1413
	if ( absint( WC_ROUNDING_PRECISION ) > $precision ) {
1414
		$precision = absint( WC_ROUNDING_PRECISION );
1415
	}
1416
	return $precision;
1417
}
1418
1419
/**
1420
 * Returns a new instance of a WC Logger.
1421
 * Use woocommerce_logging_class filter to change the logging class.
1422
 * @return WC_Logger
1423
 */
1424
function wc_get_logger() {
1425
	if ( ! class_exists( 'WC_Logger' ) ) {
1426
		include_once( dirname( __FILE__ ) . '/class-wc-logger.php' );
1427
	}
1428
	$class = apply_filters( 'woocommerce_logging_class', 'WC_Logger' );
1429
	return new $class;
1430
}
1431