Completed
Push — master ( d4cc1f...efd390 )
by Mike
08:08
created

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

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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