Completed
Pull Request — master (#11762)
by Mike
34:16
created

wc-core-functions.php ➔ wc_create_order()   D

Complexity

Conditions 9
Paths 512

Size

Total Lines 52
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 32
c 2
b 0
f 0
nc 512
nop 1
dl 0
loc 52
rs 4.4301

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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