); $attributes = $product->get_attributes(); return ( in_array( $name, array_keys( $attributes ), true ) && in_array( $value, $variation_attributes[ $attributes[ $name ]['name'] ], true ) ); } else { return false; } } } if ( ! function_exists( 'wc_tax_enabled' ) ) { /** * Are store-wide taxes enabled? * * @return bool */ function wc_tax_enabled() { return apply_filters( 'wc_tax_enabled', get_option( 'woocommerce_calc_taxes' ) === 'yes' ); } } if ( ! function_exists( 'wc_shipping_enabled' ) ) { /** * Is shipping enabled? * * @return bool */ function wc_shipping_enabled() { return apply_filters( 'wc_shipping_enabled', get_option( 'woocommerce_ship_to_countries' ) !== 'disabled' ); } } if ( ! function_exists( 'wc_prices_include_tax' ) ) { /** * Are prices inclusive of tax? * * @return bool */ function wc_prices_include_tax() { return wc_tax_enabled() && apply_filters( 'woocommerce_prices_include_tax', get_option( 'woocommerce_prices_include_tax' ) === 'yes' ); } } /** * Simple check for validating a URL, it must start with http:// or https://. * and pass FILTER_VALIDATE_URL validation. * * @param string $url to check. * @return bool */ function wc_is_valid_url( $url ) { // Must start with http:// or https://. if ( 0 !== strpos( $url, 'http://' ) && 0 !== strpos( $url, 'https://' ) ) { return false; } // Must pass validation. if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { return false; } return true; } /** * Check if the home URL is https. If it is, we don't need to do things such as 'force ssl'. * * @since 2.4.13 * @return bool */ function wc_site_is_https() { return false !== strstr( get_option( 'home' ), 'https:' ); } /** * Check if the checkout is configured for https. Look at options, WP HTTPS plugin, or the permalink itself. * * @since 2.5.0 * @return bool */ function wc_checkout_is_https() { return wc_site_is_https() || 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) || class_exists( 'WordPressHTTPS' ) || strstr( wc_get_page_permalink( 'checkout' ), 'https:' ); } /** * Checks whether the content passed contains a specific short code. * * @param string $tag Shortcode tag to check. * @return bool */ function wc_post_content_has_shortcode( $tag = '' ) { global $post; return is_singular() && is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, $tag ); } /** * Check if reviews are enabled. * * @since 3.6.0 * @return bool */ function wc_reviews_enabled() { return 'yes' === get_option( 'woocommerce_enable_reviews' ); } /** * Check if reviews ratings are enabled. * * @since 3.6.0 * @return bool */ function wc_review_ratings_enabled() { return wc_reviews_enabled() && 'yes' === get_option( 'woocommerce_enable_review_rating' ); } /** * Check if review ratings are required. * * @since 3.6.0 * @return bool */ function wc_review_ratings_required() { return 'yes' === get_option( 'woocommerce_review_rating_required' ); } /** * Check if a CSV file is valid. * * @since 3.6.5 * @param string $file File name. * @param bool $check_path If should check for the path. * @return bool */ function wc_is_file_valid_csv( $file, $check_path = true ) { /** * Filter check for CSV file path. * * @since 3.6.4 * @param bool $check_import_file_path If requires file path check. Defaults to true. * @param string $file Path of the file to be checked. */ $check_import_file_path = apply_filters( 'woocommerce_csv_importer_check_import_file_path', true, $file ); if ( $check_path && $check_import_file_path && false !== stripos( $file, '://' ) ) { return false; } /** * Filter CSV valid file types. * * @since 3.6.5 * @param array $valid_filetypes List of valid file types. */ $valid_filetypes = apply_filters( 'woocommerce_csv_import_valid_filetypes', array( 'csv' => 'text/csv', 'txt' => 'text/plain', ) ); $filetype = wp_check_filetype( $file, $valid_filetypes ); if ( in_array( $filetype['type'], $valid_filetypes, true ) ) { return true; } return false; } /** * Check if the current theme is a block theme. * * @since 6.0.0 * @return bool */ function wc_current_theme_is_fse_theme() { if ( function_exists( 'wp_is_block_theme' ) ) { return (bool) wp_is_block_theme(); } if ( function_exists( 'gutenberg_is_fse_theme' ) ) { return (bool) gutenberg_is_fse_theme(); } return false; } /** * Check if the current theme has WooCommerce support or is a FSE theme. * * @since 6.0.0 * @return bool */ function wc_current_theme_supports_woocommerce_or_fse() { return (bool) current_theme_supports( 'woocommerce' ) || wc_current_theme_is_fse_theme(); } /** * Given an element name, returns a class name. * * If the WP-related function is not defined or current theme is not a FSE theme, return empty string. * * @param string $element The name of the element. * * @since 7.0.1 * @return string */ function wc_wp_theme_get_element_class_name( $element ) { if ( wc_current_theme_is_fse_theme() && function_exists( 'wp_theme_get_element_class_name' ) ) { return wp_theme_get_element_class_name( $element ); } return ''; } /** * Given an element name, returns true or false depending on whether the * current theme has styles for that element defined in theme.json. * * If the theme is not a block theme or the WP-related function is not defined, * return false. * * @param string $element The name of the element. * * @since 7.4.0 * @return bool */ function wc_block_theme_has_styles_for_element( $element ) { if ( ! wc_current_theme_is_fse_theme() || wc_wp_theme_get_element_class_name( $element ) === '' ) { return false; } if ( function_exists( 'wp_get_global_styles' ) ) { $global_styles = wp_get_global_styles(); if ( array_key_exists( 'elements', $global_styles ) && array_key_exists( $element, $global_styles['elements'] ) ) { return is_array( $global_styles['elements'][ $element ] ); } } return false; } * @return WC_DateTime */ function wc_string_to_datetime( $time_string ) { $time_string = $time_string ?? ''; // Strings are defined in local WP timezone. Convert to UTC. if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $time_string, $date_bits ) ) { $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset(); $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset; } else { $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $time_string ) ) ) ); } $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) ); // Set local timezone or offset. if ( get_option( 'timezone_string' ) ) { $datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) ); } else { $datetime->set_utc_offset( wc_timezone_offset() ); } return $datetime; } /** * WooCommerce Timezone - helper to retrieve the timezone string for a site until. * a WP core method exists (see https://core.trac.wordpress.org/ticket/24730). * * Adapted from https://secure.php.net/manual/en/function.timezone-name-from-abbr.php#89155. * * @since 2.1 * @return string PHP timezone string for the site */ function wc_timezone_string() { // Added in WordPress 5.3 Ref https://developer.wordpress.org/reference/functions/wp_timezone_string/. if ( function_exists( 'wp_timezone_string' ) ) { return wp_timezone_string(); } // If site timezone string exists, return it. $timezone = get_option( 'timezone_string' ); if ( $timezone ) { return $timezone; } // Get UTC offset, if it isn't set then return UTC. $utc_offset = floatval( get_option( 'gmt_offset', 0 ) ); if ( ! is_numeric( $utc_offset ) || 0.0 === $utc_offset ) { return 'UTC'; } // Adjust UTC offset from hours to seconds. $utc_offset = (int) ( $utc_offset * 3600 ); // Attempt to guess the timezone string from the UTC offset. $timezone = timezone_name_from_abbr( '', $utc_offset ); if ( $timezone ) { return $timezone; } // Last try, guess timezone string manually. foreach ( timezone_abbreviations_list() as $abbr ) { foreach ( $abbr as $city ) { // WordPress restrict the use of date(), since it's affected by timezone settings, but in this case is just what we need to guess the correct timezone. if ( (bool) date( 'I' ) === (bool) $city['dst'] && $city['timezone_id'] && intval( $city['offset'] ) === $utc_offset ) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date return $city['timezone_id']; } } } // Fallback to UTC. return 'UTC'; } /** * Get timezone offset in seconds. * * @since 3.0.0 * @return float */ function wc_timezone_offset() { $timezone = get_option( 'timezone_string' ); if ( $timezone ) { $timezone_object = new DateTimeZone( $timezone ); return $timezone_object->getOffset( new DateTime( 'now' ) ); } else { return floatval( get_option( 'gmt_offset', 0 ) ) * HOUR_IN_SECONDS; } } /** * Callback which can flatten post meta (gets the first value if it's an array). * * @since 3.0.0 * @param array $value Value to flatten. * @return mixed */ function wc_flatten_meta_callback( $value ) { return is_array( $value ) ? current( $value ) : $value; } if ( ! function_exists( 'wc_rgb_from_hex' ) ) { /** * Convert RGB to HEX. * * @param mixed $color Color. * * @return array */ function wc_rgb_from_hex( $color ) { $color = str_replace( '#', '', $color ?? '000' ); // Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF". $color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color ); $rgb = array(); $rgb['R'] = hexdec( $color[0] . $color[1] ); $rgb['G'] = hexdec( $color[2] . $color[3] ); $rgb['B'] = hexdec( $color[4] . $color[5] ); return $rgb; } } if ( ! function_exists( 'wc_hex_darker' ) ) { /** * Make HEX color darker. * * @param mixed $color Color. * @param int $factor Darker factor. * Defaults to 30. * @return string */ function wc_hex_darker( $color, $factor = 30 ) { $base = wc_rgb_from_hex( $color ); $color = '#'; foreach ( $base as $k => $v ) { $amount = $v / 100; $amount = NumberUtil::round( $amount * $factor ); $new_decimal = $v - $amount; $new_hex_component = dechex( $new_decimal ); if ( strlen( $new_hex_component ) < 2 ) { $new_hex_component = '0' . $new_hex_component; } $color .= $new_hex_component; } return $color; } } if ( ! function_exists( 'wc_hex_lighter' ) ) { /** * Make HEX color lighter. * * @param mixed $color Color. * @param int $factor Lighter factor. * Defaults to 30. * @return string */ function wc_hex_lighter( $color, $factor = 30 ) { $base = wc_rgb_from_hex( $color ); $color = '#'; foreach ( $base as $k => $v ) { $amount = 255 - $v; $amount = $amount / 100; $amount = NumberUtil::round( $amount * $factor ); $new_decimal = $v + $amount; $new_hex_component = dechex( $new_decimal ); if ( strlen( $new_hex_component ) < 2 ) { $new_hex_component = '0' . $new_hex_component; } $color .= $new_hex_component; } return $color; } } if ( ! function_exists( 'wc_hex_is_light' ) ) { /** * Determine whether a hex color is light. * * @param mixed $color Color. * @return bool True if a light color. */ function wc_hex_is_light( $color ) { $hex = str_replace( '#', '', $color ?? '' ); $c_r = hexdec( substr( $hex, 0, 2 ) ); $c_g = hexdec( substr( $hex, 2, 2 ) ); $c_b = hexdec( substr( $hex, 4, 2 ) ); $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000; return $brightness > 155; } } if ( ! function_exists( 'wc_light_or_dark' ) ) { /** * Detect if we should use a light or dark color on a background color. * * @param mixed $color Color. * @param string $dark Darkest reference. * Defaults to '#000000'. * @param string $light Lightest reference. * Defaults to '#FFFFFF'. * @return string */ function wc_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) { return wc_hex_is_light( $color ) ? $dark : $light; } } if ( ! function_exists( 'wc_format_hex' ) ) { /** * Format string as hex. * * @param string $hex HEX color. * @return string|null */ function wc_format_hex( $hex ) { $hex = trim( str_replace( '#', '', $hex ?? '' ) ); if ( strlen( $hex ) === 3 ) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } return $hex ? '#' . $hex : null; } } /** * Format the postcode according to the country and length of the postcode. * * @param string $postcode Unformatted postcode. * @param string $country Base country. * @return string */ function wc_format_postcode( $postcode, $country ) { $postcode = wc_normalize_postcode( $postcode ?? '' ); switch ( $country ) { case 'CA': case 'GB': $postcode = substr_replace( $postcode, ' ', -3, 0 ); break; case 'IE': $postcode = substr_replace( $postcode, ' ', 3, 0 ); break; case 'BR': case 'PL': $postcode = substr_replace( $postcode, '-', -3, 0 ); break; case 'JP': $postcode = substr_replace( $postcode, '-', 3, 0 ); break; case 'PT': $postcode = substr_replace( $postcode, '-', 4, 0 ); break; case 'PR': case 'US': $postcode = rtrim( substr_replace( $postcode, '-', 5, 0 ), '-' ); break; case 'NL': $postcode = substr_replace( $postcode, ' ', 4, 0 ); break; case 'LV': if ( preg_match( '/(?:LV)?-?(\d+)/i', $postcode, $matches ) ) { $postcode = count( $matches ) >= 2 ? "LV-$matches[1]" : $postcode; } break; case 'DK': $postcode = preg_replace( '/^(DK)(.+)$/', '${1}-${2}', $postcode ); break; } return apply_filters( 'woocommerce_format_postcode', trim( $postcode ), $country ); } /** * Normalize postcodes. * * Remove spaces and convert characters to uppercase. * * @since 2.6.0 * @param string $postcode Postcode. * @return string */ function wc_normalize_postcode( $postcode ) { return preg_replace( '/[\s\-]/', '', trim( wc_strtoupper( $postcode ?? '' ) ) ); } /** * Format phone numbers. * * @param string $phone Phone number. * @return string */ function wc_format_phone_number( $phone ) { $phone = $phone ?? ''; if ( ! WC_Validation::is_phone( $phone ) ) { return ''; } return preg_replace( '/[^0-9\+\-\(\)\s]/', '-', preg_replace( '/[\x00-\x1F\x7F-\xFF]/', '', $phone ) ); } /** * Sanitize phone number. * Allows only numbers and "+" (plus sign). * * @since 3.6.0 * @param string $phone Phone number. * @return string */ function wc_sanitize_phone_number( $phone ) { return preg_replace( '/[^\d+]/', '', $phone ?? '' ); } /** * Wrapper for mb_strtoupper which see's if supported first. * * @since 3.1.0 * @param string $string String to format. * @return string */ function wc_strtoupper( $string ) { $string = $string ?? ''; return function_exists( 'mb_strtoupper' ) ? mb_strtoupper( $string ) : strtoupper( $string ); } /** * Make a string lowercase. * Try to use mb_strtolower() when available. * * @since 2.3 * @param string $string String to format. * @return string */ function wc_strtolower( $string ) { $string = $string ?? ''; return function_exists( 'mb_strtolower' ) ? mb_strtolower( $string ) : strtolower( $string ); } /** * Trim a string and append a suffix. * * @param string $string String to trim. * @param integer $chars Amount of characters. * Defaults to 200. * @param string $suffix Suffix. * Defaults to '...'. * @return string */ function wc_trim_string( $string, $chars = 200, $suffix = '...' ) { $string = $string ?? ''; if ( strlen( $string ) > $chars ) { if ( function_exists( 'mb_substr' ) ) { $string = mb_substr( $string, 0, ( $chars - mb_strlen( $suffix ) ) ) . $suffix; } else { $string = substr( $string, 0, ( $chars - strlen( $suffix ) ) ) . $suffix; } } return $string; } /** * Format content to display shortcodes. * * @since 2.3.0 * @param string $raw_string Raw string. * @return string */ function wc_format_content( $raw_string ) { $raw_string = $raw_string ?? ''; return apply_filters( 'woocommerce_format_content', apply_filters( 'woocommerce_short_description', $raw_string ), $raw_string ); } /** * Format product short description. * Adds support for Jetpack Markdown. * * @codeCoverageIgnore * @since 2.4.0 * @param string $content Product short description. * @return string */ function wc_format_product_short_description( $content ) { // Add support for Jetpack Markdown. if ( class_exists( 'WPCom_Markdown' ) ) { $markdown = WPCom_Markdown::get_instance(); return wpautop( $markdown->transform( $content, array( 'unslash' => false, ) ) ); } return $content; } /** * Formats curency symbols when saved in settings. * * @codeCoverageIgnore * @param string $value Option value. * @param array $option Option name. * @param string $raw_value Raw value. * @return string */ function wc_format_option_price_separators( $value, $option, $raw_value ) { return wp_kses_post( $raw_value ?? '' ); } add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_price_decimal_sep', 'wc_format_option_price_separators', 10, 3 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_price_thousand_sep', 'wc_format_option_price_separators', 10, 3 ); /** * Formats decimals when saved in settings. * * @codeCoverageIgnore * @param string $value Option value. * @param array $option Option name. * @param string $raw_value Raw value. * @return string */ function wc_format_option_price_num_decimals( $value, $option, $raw_value ) { return is_null( $raw_value ) ? 2 : absint( $raw_value ); } add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_price_num_decimals', 'wc_format_option_price_num_decimals', 10, 3 ); /** * Formats hold stock option and sets cron event up. * * @codeCoverageIgnore * @param string $value Option value. * @param array $option Option name. * @param string $raw_value Raw value. * @return string */ function wc_format_option_hold_stock_minutes( $value, $option, $raw_value ) { $value = ! empty( $raw_value ) ? absint( $raw_value ) : ''; // Allow > 0 or set to ''. wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' ); if ( '' !== $value ) { $cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $value ) ); wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' ); } return $value; } add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_hold_stock_minutes', 'wc_format_option_hold_stock_minutes', 10, 3 ); /** * Sanitize terms from an attribute text based. * * @since 2.4.5 * @param string $term Term value. * @return string */ function wc_sanitize_term_text_based( $term ) { return trim( wp_strip_all_tags( wp_unslash( $term ?? '' ) ) ); } if ( ! function_exists( 'wc_make_numeric_postcode' ) ) { /** * Make numeric postcode. * * Converts letters to numbers so we can do a simple range check on postcodes. * E.g. PE30 becomes 16050300 (P = 16, E = 05, 3 = 03, 0 = 00) * * @since 2.6.0 * @param string $postcode Regular postcode. * @return string */ function wc_make_numeric_postcode( $postcode ) { $postcode = str_replace( array( ' ', '-' ), '', $postcode ?? '' ); $postcode_length = strlen( $postcode ); $letters_to_numbers = array_merge( array( 0 ), range( 'A', 'Z' ) ); $letters_to_numbers = array_flip( $letters_to_numbers ); $numeric_postcode = ''; for ( $i = 0; $i < $postcode_length; $i ++ ) { if ( is_numeric( $postcode[ $i ] ) ) { $numeric_postcode .= str_pad( $postcode[ $i ], 2, '0', STR_PAD_LEFT ); } elseif ( isset( $letters_to_numbers[ $postcode[ $i ] ] ) ) { $numeric_postcode .= str_pad( $letters_to_numbers[ $postcode[ $i ] ], 2, '0', STR_PAD_LEFT ); } else { $numeric_postcode .= '00'; } } return $numeric_postcode; } } /** * Format the stock amount ready for display based on settings. * * @since 3.0.0 * @param WC_Product $product Product object for which the stock you need to format. * @return string */ function wc_format_stock_for_display( $product ) { $display = __( 'In stock', 'woocommerce' ); $stock_amount = $product->get_stock_quantity(); switch ( get_option( 'woocommerce_stock_format' ) ) { case 'low_amount': if ( $stock_amount <= wc_get_low_stock_amount( $product ) ) { /* translators: %s: stock amount */ $display = sprintf( __( 'Only %s left in stock', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_amount, $product ) ); } break; case '': /* translators: %s: stock amount */ $display = sprintf( __( '%s in stock', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_amount, $product ) ); break; } if ( $product->backorders_allowed() && $product->backorders_require_notification() ) { $display .= ' ' . __( '(can be backordered)', 'woocommerce' ); } return $display; } /** * Format the stock quantity ready for display. * * @since 3.0.0 * @param int $stock_quantity Stock quantity. * @param WC_Product $product Product instance so that we can pass through the filters. * @return string */ function wc_format_stock_quantity_for_display( $stock_quantity, $product ) { return apply_filters( 'woocommerce_format_stock_quantity', $stock_quantity, $product ); } /** * Format a sale price for display. * * @since 3.0.0 * @param string $regular_price Regular price. * @param string $sale_price Sale price. * @return string */ function wc_format_sale_price( $regular_price, $sale_price ) { $price = ' ' . ( is_numeric( $sale_price ) ? wc_price( $sale_price ) : $sale_price ) . ''; return apply_filters( 'woocommerce_format_sale_price', $price, $regular_price, $sale_price ); } /** * Format a price range for display. * * @param string $from Price from. * @param string $to Price to. * @return string */ function wc_format_price_range( $from, $to ) { /* translators: 1: price from 2: price to */ $price = sprintf( _x( '%1$s – %2$s', 'Price range: from-to', 'woocommerce' ), is_numeric( $from ) ? wc_price( $from ) : $from, is_numeric( $to ) ? wc_price( $to ) : $to ); return apply_filters( 'woocommerce_format_price_range', $price, $from, $to ); } /** * Format a weight for display. * * @since 3.0.0 * @param float $weight Weight. * @return string */ function wc_format_weight( $weight ) { $weight_string = wc_format_localized_decimal( $weight ); if ( ! empty( $weight_string ) ) { $weight_label = I18nUtil::get_weight_unit_label( get_option( 'woocommerce_weight_unit' ) ); $weight_string = sprintf( // translators: 1. A formatted number; 2. A label for a weight unit of measure. E.g. 2.72 kg. _x( '%1$s %2$s', 'formatted weight', 'woocommerce' ), $weight_string, $weight_label ); } else { $weight_string = __( 'N/A', 'woocommerce' ); } return apply_filters( 'woocommerce_format_weight', $weight_string, $weight ); } /** * Format dimensions for display. * * @since 3.0.0 * @param array $dimensions Array of dimensions. * @return string */ function wc_format_dimensions( $dimensions ) { $dimension_string = implode( ' × ', array_filter( array_map( 'wc_format_localized_decimal', $dimensions ) ) ); if ( ! empty( $dimension_string ) ) { $dimension_label = I18nUtil::get_dimensions_unit_label( get_option( 'woocommerce_dimension_unit' ) ); $dimension_string = sprintf( // translators: 1. A formatted number; 2. A label for a dimensions unit of measure. E.g. 3.14 cm. _x( '%1$s %2$s', 'formatted dimensions', 'woocommerce' ), $dimension_string, $dimension_label ); } else { $dimension_string = __( 'N/A', 'woocommerce' ); } return apply_filters( 'woocommerce_format_dimensions', $dimension_string, $dimensions ); } /** * Format a date for output. * * @since 3.0.0 * @param WC_DateTime $date Instance of WC_DateTime. * @param string $format Data format. * Defaults to the wc_date_format function if not set. * @return string */ function wc_format_datetime( $date, $format = '' ) { if ( ! $format ) { $format = wc_date_format(); } if ( ! is_a( $date, 'WC_DateTime' ) ) { return ''; } return $date->date_i18n( $format ); } /** * Process oEmbeds. * * @since 3.1.0 * @param string $content Content. * @return string */ function wc_do_oembeds( $content ) { global $wp_embed; $content = $wp_embed->autoembed( $content ?? '' ); return $content; } /** * Get part of a string before :. * * Used for example in shipping methods ids where they take the format * method_id:instance_id * * @since 3.2.0 * @param string $string String to extract. * @return string */ function wc_get_string_before_colon( $string ) { return trim( current( explode( ':', (string) $string ) ) ); } /** * Array merge and sum function. * * Source: https://gist.github.com/Nickology/f700e319cbafab5eaedc * * @since 3.2.0 * @return array */ function wc_array_merge_recursive_numeric() { $arrays = func_get_args(); // If there's only one array, it's already merged. if ( 1 === count( $arrays ) ) { return $arrays[0]; } // Remove any items in $arrays that are NOT arrays. foreach ( $arrays as $key => $array ) { if ( ! is_array( $array ) ) { unset( $arrays[ $key ] ); } } // We start by setting the first array as our final array. // We will merge all other arrays with this one. $final = array_shift( $arrays ); foreach ( $arrays as $b ) { foreach ( $final as $key => $value ) { // If $key does not exist in $b, then it is unique and can be safely merged. if ( ! isset( $b[ $key ] ) ) { $final[ $key ] = $value; } else { // If $key is present in $b, then we need to merge and sum numeric values in both. if ( is_numeric( $value ) && is_numeric( $b[ $key ] ) ) { // If both values for these keys are numeric, we sum them. $final[ $key ] = $value + $b[ $key ]; } elseif ( is_array( $value ) && is_array( $b[ $key ] ) ) { // If both values are arrays, we recursively call ourself. $final[ $key ] = wc_array_merge_recursive_numeric( $value, $b[ $key ] ); } else { // If both keys exist but differ in type, then we cannot merge them. // In this scenario, we will $b's value for $key is used. $final[ $key ] = $b[ $key ]; } } } // Finally, we need to merge any keys that exist only in $b. foreach ( $b as $key => $value ) { if ( ! isset( $final[ $key ] ) ) { $final[ $key ] = $value; } } } return $final; } /** * Implode and escape HTML attributes for output. * * @since 3.3.0 * @param array $raw_attributes Attribute name value pairs. * @return string */ function wc_implode_html_attributes( $raw_attributes ) { $attributes = array(); foreach ( $raw_attributes as $name => $value ) { $attributes[] = esc_attr( $name ) . '="' . esc_attr( $value ) . '"'; } return implode( ' ', $attributes ); } /** * Escape JSON for use on HTML or attribute text nodes. * * @since 3.5.5 * @param string $json JSON to escape. * @param bool $html True if escaping for HTML text node, false for attributes. Determines how quotes are handled. * @return string Escaped JSON. */ function wc_esc_json( $json, $html = false ) { return _wp_specialchars( $json, $html ? ENT_NOQUOTES : ENT_QUOTES, // Escape quotes in attribute nodes only. 'UTF-8', // json_encode() outputs UTF-8 (really just ASCII), not the blog's charset. true // Double escape entities: `&` -> `&amp;`. ); } /** * Parse a relative date option from the settings API into a standard format. * * @since 3.4.0 * @param mixed $raw_value Value stored in DB. * @return array Nicely formatted array with number and unit values. */ function wc_parse_relative_date_option( $raw_value ) { $periods = array( 'days' => __( 'Day(s)', 'woocommerce' ), 'weeks' => __( 'Week(s)', 'woocommerce' ), 'months' => __( 'Month(s)', 'woocommerce' ), 'years' => __( 'Year(s)', 'woocommerce' ), ); $value = wp_parse_args( (array) $raw_value, array( 'number' => '', 'unit' => 'days', ) ); $value['number'] = ! empty( $value['number'] ) ? absint( $value['number'] ) : ''; if ( ! in_array( $value['unit'], array_keys( $periods ), true ) ) { $value['unit'] = 'days'; } return $value; } /** * Format the endpoint slug, strip out anything not allowed in a url. * * @since 3.5.0 * @param string $raw_value The raw value. * @return string */ function wc_sanitize_endpoint_slug( $raw_value ) { return sanitize_title( $raw_value ?? '' ); } add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_checkout_pay_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_checkout_order_received_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_add_payment_method_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_delete_payment_method_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_set_default_payment_method_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_orders_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_view_order_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_downloads_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_edit_account_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_edit_address_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_payment_methods_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_myaccount_lost_password_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 ); add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_logout_endpoint', 'wc_sanitize_endpoint_slug', 10, 1 );