/** * Find a matching zone for a given package. * @since 2.6.0 * @uses wc_make_numeric_postcode() * @param object $package * @return WC_Shipping_Zone */ public static function get_zone_matching_package($package) { global $wpdb; $country = strtoupper(wc_clean($package['destination']['country'])); $state = strtoupper(wc_clean($package['destination']['state'])); $continent = strtoupper(wc_clean(WC()->countries->get_continent_code_for_country($country))); $postcode = strtoupper(wc_clean($package['destination']['postcode'])); $valid_postcodes = array_map('wc_clean', self::_get_wildcard_postcodes($postcode)); $cache_key = WC_Cache_Helper::get_cache_prefix('shipping_zones') . 'wc_shipping_zone_' . md5(sprintf('%s+%s+%s', $country, $state, $postcode)); $matching_zone_id = wp_cache_get($cache_key, 'shipping_zones'); if (false === $matching_zone_id) { // Work out criteria for our zone search $criteria = array(); $criteria[] = $wpdb->prepare("( ( location_type = 'country' AND location_code = %s )", $country); $criteria[] = $wpdb->prepare("OR ( location_type = 'state' AND location_code = %s )", $country . ':' . $state); $criteria[] = $wpdb->prepare("OR ( location_type = 'continent' AND location_code = %s ) )", $continent); // Postcode range and wildcard matching $postcode_locations = $wpdb->get_results("SELECT zone_id, location_code FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE location_type = 'postcode';"); if ($postcode_locations) { $zone_ids_with_postcode_rules = array_map('absint', wp_list_pluck($postcode_locations, 'zone_id')); $zone_id_matches = array(); foreach ($postcode_locations as $postcode_location) { $postcode_to_match = trim(strtoupper($postcode_location->location_code)); // Ranges if (strstr('-', $postcode_to_match)) { $range = array_map('trim', explode('-', $postcode_to_match)); if (sizeof($range) != 2) { continue; } if (is_numeric($range[0]) && is_numeric($range[1])) { $encoded_postcode = $postcode; $min = $range[0]; $max = $range[1]; } else { $min = wc_make_numeric_postcode($range[0]); $max = wc_make_numeric_postcode($range[1]); $min = str_pad($min, $encoded_postcode_len, '0'); $max = str_pad($max, $encoded_postcode_len, '9'); } if ($encoded_postcode >= $min && $encoded_postcode <= $max) { $zone_id_matches[] = absint($postcode_location->zone_id); } // Wildcard/standard } elseif (in_array($postcode_to_match, $valid_postcodes)) { $zone_id_matches[] = absint($postcode_location->zone_id); } } $do_not_match = array_unique(array_diff($zone_ids_with_postcode_rules, $zone_id_matches)); if ($do_not_match) { $criteria[] = "AND zones.zone_id NOT IN (" . implode(',', $do_not_match) . ")"; } } // Get matching zones $matching_zone_id = $wpdb->get_var("\n\t\t\t\tSELECT zones.zone_id FROM {$wpdb->prefix}woocommerce_shipping_zones as zones\n\t\t\t\tLEFT OUTER JOIN {$wpdb->prefix}woocommerce_shipping_zone_locations as locations ON zones.zone_id = locations.zone_id\n\t\t\t\tWHERE " . implode(' ', $criteria) . "\n\t\t\t\tORDER BY zone_order ASC LIMIT 1\n\t\t\t"); wp_cache_set($cache_key, $matching_zone_id, 'shipping_zones'); } return new WC_Shipping_Zone($matching_zone_id ? $matching_zone_id : 0); }
/** * Used by shipping zones and taxes to compare a given $postcode to stored * postcodes to find matches for numerical ranges, and wildcards. * @since 2.6.0 * @param string $postcode Postcode you want to match against stored postcodes * @param array $objects Array of postcode objects from Database * @param string $object_id_key DB column name for the ID. * @param string $object_compare_key DB column name for the value. * @param string $country Country from which this postcode belongs. Allows for formatting. * @return array Array of matching object ID and matching values. */ function wc_postcode_location_matcher($postcode, $objects, $object_id_key, $object_compare_key, $country = '') { $postcode = wc_normalize_postcode($postcode); $wildcard_postcodes = array_map('wc_clean', wc_get_wildcard_postcodes($postcode, $country)); $matches = array(); foreach ($objects as $object) { $object_id = $object->{$object_id_key}; $compare_against = $object->{$object_compare_key}; // Handle postcodes containing ranges. if (strstr($compare_against, '...')) { $range = array_map('trim', explode('...', $compare_against)); if (2 !== sizeof($range)) { continue; } list($min, $max) = $range; // If the postcode is non-numeric, make it numeric. if (!is_numeric($min) || !is_numeric($max)) { $compare = wc_make_numeric_postcode($postcode); $min = str_pad(wc_make_numeric_postcode($min), strlen($compare), '0'); $max = str_pad(wc_make_numeric_postcode($max), strlen($compare), '0'); } else { $compare = $postcode; } if ($compare >= $min && $compare <= $max) { $matches[$object_id] = isset($matches[$object_id]) ? $matches[$object_id] : array(); $matches[$object_id][] = $compare_against; } // Wildcard and standard comparison. } elseif (in_array($compare_against, $wildcard_postcodes)) { $matches[$object_id] = isset($matches[$object_id]) ? $matches[$object_id] : array(); $matches[$object_id][] = $compare_against; } } return $matches; }
/** * Alias for wc_get_shipping_zone */ function woocommerce_make_numeric_postcode($postcode) { return wc_make_numeric_postcode($postcode); }