/includes/wc-core-functions.php

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