Completed
Pull Request — master (#11762)
by Mike
13:05
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 3
Bugs 0 Features 0
Metric Value
cc 9
eloc 32
c 3
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
 * Enables template debug mode.
902
 */
903
function wc_template_debug_mode() {
904
	if ( ! defined( 'WC_TEMPLATE_DEBUG_MODE' ) ) {
905
		$status_options = get_option( 'woocommerce_status_options', array() );
906
		if ( ! empty( $status_options['template_debug_mode'] ) && current_user_can( 'manage_options' ) ) {
907
			define( 'WC_TEMPLATE_DEBUG_MODE', true );
908
		} else {
909
			define( 'WC_TEMPLATE_DEBUG_MODE', false );
910
		}
911
	}
912
}
913
add_action( 'after_setup_theme', 'wc_template_debug_mode', 20 );
914
915
/**
916
 * Formats a string in the format COUNTRY:STATE into an array.
917
 *
918
 * @since 2.3.0
919
 * @param  string $country_string
920
 * @return array
921
 */
922
function wc_format_country_state_string( $country_string ) {
923
	if ( strstr( $country_string, ':' ) ) {
924
		list( $country, $state ) = explode( ':', $country_string );
925
	} else {
926
		$country = $country_string;
927
		$state   = '';
928
	}
929
	return array(
930
		'country' => $country,
931
		'state'   => $state
932
	);
933
}
934
935
/**
936
 * Get the store's base location.
937
 *
938
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
939
 * @since 2.3.0
940
 * @return array
941
 */
942
function wc_get_base_location() {
943
	$default = apply_filters( 'woocommerce_get_base_location', get_option( 'woocommerce_default_country' ) );
944
945
	return wc_format_country_state_string( $default );
946
}
947
948
/**
949
 * Get the customer's default location.
950
 *
951
 * Filtered, and set to base location or left blank. If cache-busting,
952
 * this should only be used when 'location' is set in the querystring.
953
 *
954
 * @todo should the woocommerce_default_country option be renamed to contain 'base'?
955
 * @todo deprecate woocommerce_customer_default_location and support an array filter only to cover all cases.
956
 * @since 2.3.0
957
 * @return array
958
 */
959
function wc_get_customer_default_location() {
960
	$location = array();
961
962
	switch ( get_option( 'woocommerce_default_customer_address' ) ) {
963
		case 'geolocation_ajax' :
964
		case 'geolocation' :
965
			// Exclude common bots from geolocation by user agent.
966
			$ua = wc_get_user_agent();
967
968
			if ( ! strstr( $ua, 'bot' ) && ! strstr( $ua, 'spider' ) && ! strstr( $ua, 'crawl' ) ) {
969
				$location = WC_Geolocation::geolocate_ip( '', true, false );
970
			}
971
972
			// Base fallback.
973
			if ( empty( $location['country'] ) ) {
974
				$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
975
			}
976
		break;
977
		case 'base' :
978
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
979
		break;
980
		default :
981
			$location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', '' ) );
982
		break;
983
	}
984
985
	return apply_filters( 'woocommerce_customer_default_location_array', $location );
986
}
987
988
/**
989
 * Get user agent string.
990
 * @since  2.7.0
991
 * @return string
992
 */
993
function wc_get_user_agent() {
994
	return isset( $_SERVER['HTTP_USER_AGENT'] ) ? strtolower( $_SERVER['HTTP_USER_AGENT'] ) : '';
995
}
996
997
// This function can be removed when WP 3.9.2 or greater is required.
998
if ( ! function_exists( 'hash_equals' ) ) :
999
	/**
1000
	 * Compare two strings in constant time.
1001
	 *
1002
	 * This function was added in PHP 5.6.
1003
	 * It can leak the length of a string.
1004
	 *
1005
	 * @since 3.9.2
1006
	 *
1007
	 * @param string $a Expected string.
1008
	 * @param string $b Actual string.
1009
	 * @return bool Whether strings are equal.
1010
	 */
1011
	function hash_equals( $a, $b ) {
1012
		$a_length = strlen( $a );
1013
		if ( $a_length !== strlen( $b ) ) {
1014
			return false;
1015
		}
1016
		$result = 0;
1017
1018
		// Do not attempt to "optimize" this.
1019
		for ( $i = 0; $i < $a_length; $i++ ) {
1020
			$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
1021
		}
1022
1023
		return $result === 0;
1024
	}
1025
endif;
1026
1027
/**
1028
 * Generate a rand hash.
1029
 *
1030
 * @since  2.4.0
1031
 * @return string
1032
 */
1033
function wc_rand_hash() {
1034
	if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
1035
		return bin2hex( openssl_random_pseudo_bytes( 20 ) );
1036
	} else {
1037
		return sha1( wp_rand() );
1038
	}
1039
}
1040
1041
/**
1042
 * WC API - Hash.
1043
 *
1044
 * @since  2.4.0
1045
 * @param  string $data
1046
 * @return string
1047
 */
1048
function wc_api_hash( $data ) {
1049
	return hash_hmac( 'sha256', $data, 'wc-api' );
1050
}
1051
1052
/**
1053
 * Find all possible combinations of values from the input array and return in a logical order.
1054
 * @since 2.5.0
1055
 * @param array $input
1056
 * @return array
1057
 */
1058
function wc_array_cartesian( $input ) {
1059
	$input   = array_filter( $input );
1060
	$results = array();
1061
	$indexes = array();
1062
	$index   = 0;
1063
1064
	// Generate indexes from keys and values so we have a logical sort order
1065
	foreach ( $input as $key => $values ) {
1066
		foreach ( $values as $value ) {
1067
			$indexes[ $key ][ $value ] = $index++;
1068
		}
1069
	}
1070
1071
	// Loop over the 2D array of indexes and generate all combinations
1072
	foreach ( $indexes as $key => $values ) {
1073
		// When result is empty, fill with the values of the first looped array
1074
		if ( empty( $results ) ) {
1075
			foreach ( $values as $value ) {
1076
				$results[] = array( $key => $value );
1077
			}
1078
1079
		// Second and subsequent input sub-array merging.
1080
		} else {
1081
			foreach ( $results as $result_key => $result ) {
1082
				foreach ( $values as $value ) {
1083
					// If the key is not set, we can set it
1084
					if ( ! isset( $results[ $result_key ][ $key ] ) ) {
1085
						$results[ $result_key ][ $key ] = $value;
1086
					// If the key is set, we can add a new combination to the results array
1087
					} else {
1088
						$new_combination         = $results[ $result_key ];
1089
						$new_combination[ $key ] = $value;
1090
						$results[]               = $new_combination;
1091
					}
1092
				}
1093
			}
1094
		}
1095
	}
1096
1097
	// Sort the indexes
1098
	arsort( $results );
1099
1100
	// Convert indexes back to values
1101
	foreach ( $results as $result_key => $result ) {
1102
		$converted_values = array();
1103
1104
		// Sort the values
1105
		arsort( $results[ $result_key ] );
1106
1107
		// Convert the values
1108
		foreach ( $results[ $result_key ] as $key => $value ) {
1109
			$converted_values[ $key ] = array_search( $value, $indexes[ $key ] );
1110
		}
1111
1112
		$results[ $result_key ] = $converted_values;
1113
	}
1114
1115
	return $results;
1116
}
1117
1118
/**
1119
 * Run a MySQL transaction query, if supported.
1120
 * @param string $type start (default), commit, rollback
1121
 * @since 2.5.0
1122
 */
1123
function wc_transaction_query( $type = 'start' ) {
1124
	global $wpdb;
1125
1126
	$wpdb->hide_errors();
1127
1128
	if ( ! defined( 'WC_USE_TRANSACTIONS' ) ) {
1129
		define( 'WC_USE_TRANSACTIONS', true );
1130
	}
1131
1132
	if ( WC_USE_TRANSACTIONS ) {
1133
		switch ( $type ) {
1134
			case 'commit' :
1135
				$wpdb->query( 'COMMIT' );
1136
				break;
1137
			case 'rollback' :
1138
				$wpdb->query( 'ROLLBACK' );
1139
				break;
1140
			default :
1141
				$wpdb->query( 'START TRANSACTION' );
1142
			break;
1143
		}
1144
	}
1145
}
1146
1147
/**
1148
 * Gets the url to the cart page.
1149
 *
1150
 * @since  2.5.0
1151
 *
1152
 * @return string Url to cart page
1153
 */
1154
function wc_get_cart_url() {
1155
	return apply_filters( 'woocommerce_get_cart_url', wc_get_page_permalink( 'cart' ) );
1156
}
1157
1158
/**
1159
 * Gets the url to the checkout page.
1160
 *
1161
 * @since  2.5.0
1162
 *
1163
 * @return string Url to checkout page
1164
 */
1165
function wc_get_checkout_url() {
1166
	$checkout_url = wc_get_page_permalink( 'checkout' );
1167 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...
1168
		// Force SSL if needed
1169
		if ( is_ssl() || 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) ) {
1170
			$checkout_url = str_replace( 'http:', 'https:', $checkout_url );
1171
		}
1172
	}
1173
1174
	return apply_filters( 'woocommerce_get_checkout_url', $checkout_url );
1175
}
1176
1177
/**
1178
 * Register a shipping method.
1179
 *
1180
 * @since 1.5.7
1181
 * @param string|object $shipping_method class name (string) or a class object.
1182
 */
1183
function woocommerce_register_shipping_method( $shipping_method ) {
1184
	WC()->shipping->register_shipping_method( $shipping_method );
1185
}
1186
1187
if ( ! function_exists( 'wc_get_shipping_zone' ) ) {
1188
	/**
1189
	 * Get the shipping zone matching a given package from the cart.
1190
	 *
1191
	 * @since  2.6.0
1192
	 * @uses   WC_Shipping_Zones::get_zone_matching_package
1193
	 * @param  array $package
1194
	 * @return WC_Shipping_Zone
1195
	 */
1196
	function wc_get_shipping_zone( $package ) {
1197
		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...
1198
	}
1199
}
1200
1201
/**
1202
 * Get a nice name for credit card providers.
1203
 *
1204
 * @since  2.6.0
1205
 * @param  string $type Provider Slug/Type
1206
 * @return string
1207
 */
1208
function wc_get_credit_card_type_label( $type ) {
1209
	// Normalize
1210
	$type = strtolower( $type );
1211
	$type = str_replace( '-', ' ', $type );
1212
	$type = str_replace( '_', ' ', $type );
1213
1214
	$labels = apply_filters( 'wocommerce_credit_card_type_labels', array(
1215
		'mastercard'       => __( 'MasterCard', 'woocommerce' ),
1216
		'visa'             => __( 'Visa', 'woocommerce' ),
1217
		'discover'         => __( 'Discover', 'woocommerce' ),
1218
		'american express' => __( 'American Express', 'woocommerce' ),
1219
		'diners'           => __( 'Diners', 'woocommerce' ),
1220
		'jcb'              => __( 'JCB', 'woocommerce' ),
1221
	) );
1222
1223
	return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
1224
}
1225
1226
/**
1227
 * Outputs a "back" link so admin screens can easily jump back a page.
1228
 *
1229
 * @param string $label Title of the page to return to.
1230
 * @param string $url   URL of the page to return to.
1231
 */
1232
function wc_back_link( $label, $url ) {
1233
	echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" title="' . esc_attr( $label ) . '">&#x2934;</a></small>';
1234
}
1235
1236
/**
1237
 * Display a WooCommerce help tip.
1238
 *
1239
 * @since  2.5.0
1240
 *
1241
 * @param  string $tip        Help tip text
1242
 * @param  bool   $allow_html Allow sanitized HTML if true or escape
1243
 * @return string
1244
 */
1245
function wc_help_tip( $tip, $allow_html = false ) {
1246
	if ( $allow_html ) {
1247
		$tip = wc_sanitize_tooltip( $tip );
1248
	} else {
1249
		$tip = esc_attr( $tip );
1250
	}
1251
1252
	return '<span class="woocommerce-help-tip" data-tip="' . $tip . '"></span>';
1253
}
1254
1255
/**
1256
 * Return a list of potential postcodes for wildcard searching.
1257
 * @since 2.6.0
1258
 * @param  string $postcode
1259
 * @param  string $country to format postcode for matching.
1260
 * @return string[]
1261
 */
1262
function wc_get_wildcard_postcodes( $postcode, $country = '' ) {
1263
	$postcodes       = array( $postcode );
1264
	$postcode        = wc_format_postcode( $postcode, $country );
1265
	$postcodes[]     = $postcode;
1266
	$postcode_length = strlen( $postcode );
1267
1268
	for ( $i = 0; $i < $postcode_length; $i ++ ) {
1269
		$postcodes[] = substr( $postcode, 0, ( $i + 1 ) * -1 ) . '*';
1270
	}
1271
1272
	return $postcodes;
1273
}
1274
1275
/**
1276
 * Used by shipping zones and taxes to compare a given $postcode to stored
1277
 * postcodes to find matches for numerical ranges, and wildcards.
1278
 * @since 2.6.0
1279
 * @param string $postcode Postcode you want to match against stored postcodes
1280
 * @param array  $objects Array of postcode objects from Database
1281
 * @param string $object_id_key DB column name for the ID.
1282
 * @param string $object_compare_key DB column name for the value.
1283
 * @param string $country Country from which this postcode belongs. Allows for formatting.
1284
 * @return array Array of matching object ID and matching values.
1285
 */
1286
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key, $country = '' ) {
1287
	$postcode           = wc_normalize_postcode( $postcode );
1288
	$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode, $country ) );
1289
	$matches            = array();
1290
1291
	foreach ( $objects as $object ) {
1292
		$object_id       = $object->$object_id_key;
1293
		$compare_against = $object->$object_compare_key;
1294
1295
		// Handle postcodes containing ranges.
1296
		if ( strstr( $compare_against, '...' ) ) {
1297
			$range = array_map( 'trim', explode( '...', $compare_against ) );
1298
1299
			if ( 2 !== sizeof( $range ) ) {
1300
				continue;
1301
			}
1302
1303
			list( $min, $max ) = $range;
1304
1305
			// If the postcode is non-numeric, make it numeric.
1306
			if ( ! is_numeric( $min ) || ! is_numeric( $max ) ) {
1307
				$compare = wc_make_numeric_postcode( $postcode );
1308
				$min     = str_pad( wc_make_numeric_postcode( $min ), strlen( $compare ), '0' );
1309
				$max     = str_pad( wc_make_numeric_postcode( $max ), strlen( $compare ), '0' );
1310
			} else {
1311
				$compare = $postcode;
1312
			}
1313
1314
			if ( $compare >= $min && $compare <= $max ) {
1315
				$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1316
				$matches[ $object_id ][] = $compare_against;
1317
			}
1318
1319
		// Wildcard and standard comparison.
1320
		} elseif ( in_array( $compare_against, $wildcard_postcodes ) ) {
1321
			$matches[ $object_id ]   = isset( $matches[ $object_id ] ) ? $matches[ $object_id ]: array();
1322
			$matches[ $object_id ][] = $compare_against;
1323
		}
1324
	}
1325
1326
	return $matches;
1327
}
1328
1329
/**
1330
 * Gets number of shipping methods currently enabled. Used to identify if
1331
 * shipping is configured.
1332
 *
1333
 * @since  2.6.0
1334
 * @param  bool $include_legacy Count legacy shipping methods too.
1335
 * @return int
1336
 */
1337
function wc_get_shipping_method_count( $include_legacy = false ) {
1338
	global $wpdb;
1339
1340
	$transient_name = 'wc_shipping_method_count_' . ( $include_legacy ? 1 : 0 ) . '_' . WC_Cache_Helper::get_transient_version( 'shipping' );
1341
	$method_count   = get_transient( $transient_name );
1342
1343
	if ( false === $method_count ) {
1344
		$method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) );
1345
1346
		if ( $include_legacy ) {
1347
			// Count activated methods that don't support shipping zones.
1348
			$methods = WC()->shipping->get_shipping_methods();
1349
1350
			foreach ( $methods as $method ) {
1351
				if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) {
1352
					$method_count++;
1353
				}
1354
			}
1355
		}
1356
1357
		set_transient( $transient_name, $method_count, DAY_IN_SECONDS * 30 );
1358
	}
1359
1360
	return absint( $method_count );
1361
}
1362
1363
/**
1364
 * Wrapper for set_time_limit to see if it is enabled.
1365
 * @since 2.6.0
1366
 */
1367
function wc_set_time_limit( $limit = 0 ) {
1368
	if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
1369
		@set_time_limit( $limit );
1370
	}
1371
}
1372
1373
/**
1374
 * Used to sort products attributes with uasort.
1375
 * @since 2.6.0
1376
 */
1377
function wc_product_attribute_uasort_comparison( $a, $b ) {
1378
	if ( $a['position'] == $b['position'] ) {
1379
		return 0;
1380
	}
1381
	return ( $a['position'] < $b['position'] ) ? -1 : 1;
1382
}
1383
1384
/**
1385
 * Used to sort shipping zone methods with uasort.
1386
 * @since 2.7.0
1387
 */
1388
function wc_shipping_zone_method_order_uasort_comparison( $a, $b ) {
1389
	if ( $a->method_order === $b->method_order ) {
1390
		return 0;
1391
	}
1392
	return ( $a->method_order < $b->method_order ) ? -1 : 1;
1393
}
1394
1395
1396
/**
1397
 * Get rounding precision for internal WC calculations.
1398
 * Will increase the precision of wc_get_price_decimals by 2 decimals, unless WC_ROUNDING_PRECISION is set to a higher number.
1399
 *
1400
 * @since 2.6.3
1401
 * @return int
1402
 */
1403
function wc_get_rounding_precision() {
1404
	$precision = wc_get_price_decimals() + 2;
1405
	if ( absint( WC_ROUNDING_PRECISION ) > $precision ) {
1406
		$precision = absint( WC_ROUNDING_PRECISION );
1407
	}
1408
	return $precision;
1409
}
1410
1411
/**
1412
 * Returns a new instance of a WC Logger.
1413
 * Use woocommerce_logging_class filter to change the logging class.
1414
 * @return WC_Logger
1415
 */
1416
function wc_get_logger() {
1417
	if ( ! class_exists( 'WC_Logger' ) ) {
1418
		include_once( dirname( __FILE__ ) . '/class-wc-logger.php' );
1419
	}
1420
	$class = apply_filters( 'woocommerce_logging_class', 'WC_Logger' );
1421
	return new $class;
1422
}
1423
1424
/**
1425
 * Runs a deprecated action with notice only if used.
1426
 * @since  2.7.0
1427
 * @param  string $action
1428
 * @param  array $args
1429
 * @param  string $deprecated_in
1430
 * @param  string $replacement
1431
 */
1432
function wc_do_deprecated_action( $action, $args, $deprecated_in, $replacement ) {
1433
	if ( has_action( $action ) ) {
1434
		_deprecated_function( 'Action: ' . $action, $deprecated_in, $replacement );
1435
		do_action_ref_array( $action, $args );
1436
	}
1437
}
1438