1 <?php
  2 /**
  3  * WooCommerce Formatting
  4  *
  5  * Functions for formatting data.
  6  *
  7  * @author      WooThemes
  8  * @category    Core
  9  * @package     WooCommerce/Functions
 10  * @version     2.1.0
 11  */
 12 
 13 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 14 
 15 /**
 16  * Sanitize taxonomy names. Slug format (no spaces, lowercase).
 17  *
 18  * Doesn't use sanitize_title as this destroys utf chars.
 19  *
 20  * @access public
 21  * @param mixed $taxonomy
 22  * @return string
 23  */
 24 function wc_sanitize_taxonomy_name( $taxonomy ) {
 25     $filtered = strtolower( remove_accents( stripslashes( strip_tags( $taxonomy ) ) ) );
 26     $filtered = preg_replace( '/&.+?;/', '', $filtered ); // Kill entities
 27     $filtered = str_replace( array( '.', '\'', '"' ), '', $filtered ); // Kill quotes and full stops.
 28     $filtered = str_replace( array( ' ', '_' ), '-', $filtered ); // Replace spaces and underscores.
 29 
 30     return apply_filters( 'sanitize_taxonomy_name', $filtered, $taxonomy );
 31 }
 32 
 33 /**
 34  * Gets the filename part of a download URL
 35  *
 36  * @access public
 37  * @param string $file_url
 38  * @return string
 39  */
 40 function wc_get_filename_from_url( $file_url ) {
 41     $parts = parse_url( $file_url );
 42     if ( isset( $parts['path'] ) ) {
 43         return basename( $parts['path'] );
 44     }
 45 }
 46 
 47 /**
 48  * Normalise dimensions, unify to cm then convert to wanted unit value
 49  *
 50  * Usage: wc_get_dimension(55, 'in');
 51  *
 52  * @access public
 53  * @param mixed $dim
 54  * @param mixed $to_unit 'in', 'm', 'cm', 'm'
 55  * @return float
 56  */
 57 function wc_get_dimension( $dim, $to_unit ) {
 58 
 59     $from_unit  = strtolower( get_option( 'woocommerce_dimension_unit' ) );
 60     $to_unit    = strtolower( $to_unit );
 61 
 62     // Unify all units to cm first
 63     if ( $from_unit !== $to_unit ) {
 64 
 65         switch ( $from_unit ) {
 66             case 'in':
 67                 $dim *= 2.54;
 68             break;
 69             case 'm':
 70                 $dim *= 100;
 71             break;
 72             case 'mm':
 73                 $dim *= 0.1;
 74             break;
 75             case 'yd':
 76                 $dim *= 91.44;
 77             break;
 78         }
 79 
 80         // Output desired unit
 81         switch ( $to_unit ) {
 82             case 'in':
 83                 $dim *= 0.3937;
 84             break;
 85             case 'm':
 86                 $dim *= 0.01;
 87             break;
 88             case 'mm':
 89                 $dim *= 10;
 90             break;
 91             case 'yd':
 92                 $dim *= 0.010936133;
 93             break;
 94         }
 95     }
 96     return ( $dim < 0 ) ? 0 : $dim;
 97 }
 98 
 99 /**
100  * Normalise weights, unify to cm then convert to wanted unit value
101  *
102  * Usage: wc_get_weight(55, 'kg');
103  *
104  * @access public
105  * @param mixed $weight
106  * @param mixed $to_unit 'g', 'kg', 'lbs'
107  * @return float
108  */
109 function wc_get_weight( $weight, $to_unit ) {
110 
111     $from_unit  = strtolower( get_option('woocommerce_weight_unit') );
112     $to_unit    = strtolower( $to_unit );
113 
114     //Unify all units to kg first
115     if ( $from_unit !== $to_unit ) {
116 
117         switch ( $from_unit ) {
118             case 'g':
119                 $weight *= 0.001;
120             break;
121             case 'lbs':
122                 $weight *= 0.4536;
123             break;
124             case 'oz':
125                 $weight *= 0.0283;
126             break;
127         }
128 
129         // Output desired unit
130         switch ( $to_unit ) {
131             case 'g':
132                 $weight *= 1000;
133             break;
134             case 'lbs':
135                 $weight *= 2.2046;
136             break;
137             case 'oz':
138                 $weight *= 35.274;
139             break;
140         }
141     }
142     return ( $weight < 0 ) ? 0 : $weight;
143 }
144 
145 /**
146  * Trim trailing zeros off prices.
147  *
148  * @access public
149  * @param mixed $price
150  * @return string
151  */
152 function wc_trim_zeros( $price ) {
153     return preg_replace( '/' . preg_quote( get_option( 'woocommerce_price_decimal_sep' ), '/' ) . '0++$/', '', $price );
154 }
155 
156 /**
157  * Round a tax amount
158  *
159  * @access public
160  * @param mixed $price
161  * @return string
162  */
163 function wc_round_tax_total( $tax ) {
164     $dp = (int) get_option( 'woocommerce_price_num_decimals' );
165 
166     if ( version_compare( phpversion(), '5.3', '<' ) ) {
167         $tax = round( $tax, $dp );
168     } else {
169         $tax = round( $tax, $dp, WC_TAX_ROUNDING_MODE );
170     }
171     return $tax;
172 }
173 
174 /**
175  * Format decimal numbers ready for DB storage
176  *
177  * Sanitize, remove locale formatting, and optionally round + trim off zeros
178  *
179  * @param  float|string $number Expects either a float or a string with a decimal separator only (no thousands)
180  * @param  mixed $dp number of decimal points to use, blank to use woocommerce_price_num_decimals, or false to avoid all rounding.
181  * @param  boolean $trim_zeros from end of string
182  * @return string
183  */
184 function wc_format_decimal( $number, $dp = false, $trim_zeros = false ) {
185     // Remove locale from string
186     if ( ! is_float( $number ) ) {
187         $locale   = localeconv();
188         $decimals = array( get_option( 'woocommerce_price_decimal_sep' ), $locale['decimal_point'], $locale['mon_decimal_point'] );
189         $number   = wc_clean( str_replace( $decimals, '.', $number ) );
190     }
191 
192     // DP is false - don't use number format, just return a string in our format
193     if ( $dp !== false ) {
194         $dp     = intval( $dp == "" ? get_option( 'woocommerce_price_num_decimals' ) : $dp );
195         $number = number_format( floatval( $number ), $dp, '.', '' );
196     }
197 
198     if ( $trim_zeros && strstr( $number, '.' ) ) {
199         $number = rtrim( rtrim( $number, '0' ), '.' );
200     }
201 
202     return $number;
203 }
204 
205 /**
206  * Convert a float to a string without locale formatting which PHP adds when changing floats to strings
207  * @param  float $float
208  * @return string
209  */
210 function wc_float_to_string( $float ) {
211     if ( ! is_float( $float ) ) {
212         return $float;
213     }
214 
215     $locale = localeconv();
216     $string = strval( $float );
217     $string = str_replace( $locale['decimal_point'], '.', $string );
218 
219     return $string;
220 }
221 
222 /**
223  * Format a price with WC Currency Locale settings
224  * @param  string $value
225  * @return string
226  */
227 function wc_format_localized_price( $value ) {
228     return str_replace( '.', get_option( 'woocommerce_price_decimal_sep' ), strval( $value ) );
229 }
230 
231 /**
232  * Format a decimal with PHP Locale settings
233  * @param  string $value
234  * @return string
235  */
236 function wc_format_localized_decimal( $value ) {
237     $locale = localeconv();
238     return str_replace( '.', $locale['decimal_point'], strval( $value ) );
239 }
240 
241 /**
242  * Clean variables
243  *
244  * @access public
245  * @param string $var
246  * @return string
247  */
248 function wc_clean( $var ) {
249     return sanitize_text_field( $var );
250 }
251 
252 /**
253  * Merge two arrays
254  *
255  * @access public
256  * @param array $a1
257  * @param array $a2
258  * @return array
259  */
260 function wc_array_overlay( $a1, $a2 ) {
261     foreach( $a1 as $k => $v ) {
262         if ( ! array_key_exists( $k, $a2 ) ) {
263             continue;
264         }
265         if ( is_array( $v ) && is_array( $a2[ $k ] ) ) {
266             $a1[ $k ] = wc_array_overlay( $v, $a2[ $k ] );
267         } else {
268             $a1[ $k ] = $a2[ $k ];
269         }
270     }
271     return $a1;
272 }
273 
274 /**
275  * Get the price format depending on the currency position
276  *
277  * @return string
278  */
279 function get_woocommerce_price_format() {
280     $currency_pos = get_option( 'woocommerce_currency_pos' );
281 
282     switch ( $currency_pos ) {
283         case 'left' :
284             $format = '%1$s%2$s';
285         break;
286         case 'right' :
287             $format = '%2$s%1$s';
288         break;
289         case 'left_space' :
290             $format = '%1$s&nbsp;%2$s';
291         break;
292         case 'right_space' :
293             $format = '%2$s&nbsp;%1$s';
294         break;
295     }
296 
297     return apply_filters( 'woocommerce_price_format', $format, $currency_pos );
298 }
299 
300 /**
301  * Format the price with a currency symbol.
302  *
303  * @access public
304  * @param float $price
305  * @param array $args (default: array())
306  * @return string
307  */
308 function wc_price( $price, $args = array() ) {
309     extract( shortcode_atts( array(
310         'ex_tax_label'  => '0'
311     ), $args ) );
312 
313     $return          = '';
314     $num_decimals    = absint( get_option( 'woocommerce_price_num_decimals' ) );
315     $currency        = isset( $args['currency'] ) ? $args['currency'] : '';
316     $currency_symbol = get_woocommerce_currency_symbol($currency);
317     $decimal_sep     = wp_specialchars_decode( stripslashes( get_option( 'woocommerce_price_decimal_sep' ) ), ENT_QUOTES );
318     $thousands_sep   = wp_specialchars_decode( stripslashes( get_option( 'woocommerce_price_thousand_sep' ) ), ENT_QUOTES );
319 
320     $price           = apply_filters( 'raw_woocommerce_price', floatval( $price ) );
321     $price           = apply_filters( 'formatted_woocommerce_price', number_format( $price, $num_decimals, $decimal_sep, $thousands_sep ), $price, $num_decimals, $decimal_sep, $thousands_sep );
322 
323     if ( apply_filters( 'woocommerce_price_trim_zeros', false ) && $num_decimals > 0 ) {
324         $price = wc_trim_zeros( $price );
325     }
326 
327     $return = '<span class="amount">' . sprintf( get_woocommerce_price_format(), $currency_symbol, $price ) . '</span>';
328 
329     if ( $ex_tax_label && get_option( 'woocommerce_calc_taxes' ) == 'yes' ) {
330         $return .= ' <small>' . WC()->countries->ex_tax_or_vat() . '</small>';
331     }
332 
333     return $return;
334 }
335 
336 /**
337  * let_to_num function.
338  *
339  * This function transforms the php.ini notation for numbers (like '2M') to an integer.
340  *
341  * @access public
342  * @param $size
343  * @return int
344  */
345 function wc_let_to_num( $size ) {
346     $l      = substr( $size, -1 );
347     $ret    = substr( $size, 0, -1 );
348     switch( strtoupper( $l ) ) {
349         case 'P':
350             $ret *= 1024;
351         case 'T':
352             $ret *= 1024;
353         case 'G':
354             $ret *= 1024;
355         case 'M':
356             $ret *= 1024;
357         case 'K':
358             $ret *= 1024;
359     }
360     return $ret;
361 }
362 
363 /**
364  * WooCommerce Date Format - Allows to change date format for everything WooCommerce
365  *
366  * @access public
367  * @return string
368  */
369 function wc_date_format() {
370     return apply_filters( 'woocommerce_date_format', get_option( 'date_format' ) );
371 }
372 
373 /**
374  * WooCommerce Time Format - Allows to change time format for everything WooCommerce
375  *
376  * @access public
377  * @return string
378  */
379 function wc_time_format() {
380     return apply_filters( 'woocommerce_time_format', get_option( 'time_format' ) );
381 }
382 
383 /**
384  * WooCommerce Timezone - helper to retrieve the timezone string for a site until
385  * a WP core method exists (see http://core.trac.wordpress.org/ticket/24730)
386  *
387  * Adapted from http://www.php.net/manual/en/function.timezone-name-from-abbr.php#89155
388  *
389  * @since 2.1
390  * @access public
391  * @return string a valid PHP timezone string for the site
392  */
393 function wc_timezone_string() {
394 
395     // if site timezone string exists, return it
396     if ( $timezone = get_option( 'timezone_string' ) ) {
397         return $timezone;
398     }
399 
400     // get UTC offset, if it isn't set then return UTC
401     if ( 0 === ( $utc_offset = get_option( 'gmt_offset', 0 ) ) ) {
402         return 'UTC';
403     }
404 
405     // adjust UTC offset from hours to seconds
406     $utc_offset *= 3600;
407 
408     // attempt to guess the timezone string from the UTC offset
409     $timezone = timezone_name_from_abbr( '', $utc_offset );
410 
411     // last try, guess timezone string manually
412     if ( false === $timezone ) {
413 
414         $is_dst = date( 'I' );
415 
416         foreach ( timezone_abbreviations_list() as $abbr ) {
417             foreach ( $abbr as $city ) {
418 
419                 if ( $city['dst'] == $is_dst && $city['offset'] == $utc_offset ) {
420                     return $city['timezone_id'];
421                 }
422             }
423         }
424     }
425 
426     // fallback to UTC
427     return 'UTC';
428 }
429 
430 if ( ! function_exists( 'wc_rgb_from_hex' ) ) {
431 
432     /**
433      * Hex darker/lighter/contrast functions for colours
434      *
435      * @access public
436      * @param mixed $color
437      * @return string
438      */
439     function wc_rgb_from_hex( $color ) {
440         $color = str_replace( '#', '', $color );
441         // Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF"
442         $color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color );
443 
444         $rgb['R'] = hexdec( $color{0}.$color{1} );
445         $rgb['G'] = hexdec( $color{2}.$color{3} );
446         $rgb['B'] = hexdec( $color{4}.$color{5} );
447         return $rgb;
448     }
449 }
450 
451 if ( ! function_exists( 'wc_hex_darker' ) ) {
452 
453     /**
454      * Hex darker/lighter/contrast functions for colours
455      *
456      * @access public
457      * @param mixed $color
458      * @param int $factor (default: 30)
459      * @return string
460      */
461     function wc_hex_darker( $color, $factor = 30 ) {
462         $base = wc_rgb_from_hex( $color );
463         $color = '#';
464 
465         foreach ($base as $k => $v) :
466             $amount = $v / 100;
467             $amount = round($amount * $factor);
468             $new_decimal = $v - $amount;
469 
470             $new_hex_component = dechex($new_decimal);
471             if(strlen($new_hex_component) < 2) :
472                 $new_hex_component = "0".$new_hex_component;
473             endif;
474             $color .= $new_hex_component;
475         endforeach;
476 
477         return $color;
478     }
479 }
480 
481 if ( ! function_exists( 'wc_hex_lighter' ) ) {
482 
483     /**
484      * Hex darker/lighter/contrast functions for colours
485      *
486      * @access public
487      * @param mixed $color
488      * @param int $factor (default: 30)
489      * @return string
490      */
491     function wc_hex_lighter( $color, $factor = 30 ) {
492         $base = wc_rgb_from_hex( $color );
493         $color = '#';
494 
495         foreach ($base as $k => $v) :
496             $amount = 255 - $v;
497             $amount = $amount / 100;
498             $amount = round($amount * $factor);
499             $new_decimal = $v + $amount;
500 
501             $new_hex_component = dechex($new_decimal);
502             if(strlen($new_hex_component) < 2) :
503                 $new_hex_component = "0".$new_hex_component;
504             endif;
505             $color .= $new_hex_component;
506         endforeach;
507 
508         return $color;
509     }
510 }
511 
512 if ( ! function_exists( 'wc_light_or_dark' ) ) {
513 
514     /**
515      * Detect if we should use a light or dark colour on a background colour
516      *
517      * @access public
518      * @param mixed $color
519      * @param string $dark (default: '#000000')
520      * @param string $light (default: '#FFFFFF')
521      * @return string
522      */
523     function wc_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
524         //return ( hexdec( $color ) > 0xffffff / 2 ) ? $dark : $light;
525         $hex = str_replace( '#', '', $color );
526 
527         $c_r = hexdec( substr( $hex, 0, 2 ) );
528         $c_g = hexdec( substr( $hex, 2, 2 ) );
529         $c_b = hexdec( substr( $hex, 4, 2 ) );
530         $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
531 
532         return $brightness > 155 ? $dark : $light;
533     }
534 }
535 
536 if ( ! function_exists( 'wc_format_hex' ) ) {
537 
538     /**
539      * Format string as hex
540      *
541      * @access public
542      * @param string $hex
543      * @return string
544      */
545     function wc_format_hex( $hex ) {
546 
547         $hex = trim( str_replace( '#', '', $hex ) );
548 
549         if ( strlen( $hex ) == 3 ) {
550             $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
551         }
552 
553         if ( $hex ) return '#' . $hex;
554     }
555 }
556 
557 /**
558  * Format the postcode according to the country and length of the postcode
559  *
560  * @param   string  postcode
561  * @param   string  country
562  * @return  string  formatted postcode
563  */
564 function wc_format_postcode( $postcode, $country ) {
565     $postcode = strtoupper(trim($postcode));
566     $postcode = trim(preg_replace('/[\s]/', '', $postcode));
567 
568     if ( in_array( $country, array('GB', 'CA') ) ) {
569         $postcode = trim( substr_replace( $postcode, ' ', -3, 0 ) );
570     }
571 
572     return $postcode;
573 }
574 
575 /**
576  * format_phone function.
577  *
578  * @access public
579  * @param mixed $tel
580  * @return string
581  */
582 function wc_format_phone_number( $tel ) {
583     $tel = str_replace( '.', '-', $tel );
584     return $tel;
585 }
586 
WooCommerce API documentation generated by ApiGen 2.8.0