public function tablerate($table) { $Order = ShoppOrder(); $Address = $Order->Shipping; $countries = ShoppLookup::countries(); $zones = ShoppLookup::country_zones(); $target = array('region' => false, 'country' => false, 'area' => false, 'zone' => false, 'postcode' => false); // Prepare address for comparison $target['region'] = ShoppLookup::region($Address->country, int); $target['country'] = $Address->country; if (isset($Address->postcode) && !empty($Address->postcode)) { $target['postcode'] = $Address->postcode; $Address->postmap(); } if (isset($Address->state) && !empty($Address->state)) { $target['zone'] = $Address->state; $areas = Lookup::country_areas(); if (isset($areas[$Address->country]) && !empty($areas[$Address->country])) { $target['area'] = array(); foreach ($areas[$Address->country] as $areaname => $areazones) { if (!in_array($Address->state, $areazones)) { continue; } $target['area'][] = $areaname; } rsort($target['area']); if (empty($target['area'])) { $target['area'] = false; } } else { $target['area'] = $target['zone']; } } // Sort table rules more specific to more generic matching usort($table, array('ShippingFramework', '_sorttable')); // Evaluate each destination rule foreach ($table as $index => $rate) { $r = Shopp::floatval(isset($rate['rate']) ? $rate['rate'] : 0); if (isset($rate['tiers'])) { $r = $rate['tiers']; usort($r, array('ShippingFramework', '_sorttier')); } $dr = strpos($rate['destination'], ',') !== false ? explode(',', $rate['destination']) : array($rate['destination']); $k = array_keys(array_slice($target, 0, count($dr))); $rule = array_combine($k, $dr); if (isset($rate['postcode']) && !empty($rate['postcode']) && '*' != $rate['postcode']) { $rule['postcode'] = $rate['postcode']; } $match = array_intersect_key($target, $rule); $d = array_diff($rule, $match); // Use the rate if the destination rule is for anywhere if ('*' == $rule['region']) { return $r; } // Exact match FTW! if (empty($d)) { return $r; } // Handle special case for area matching if (!empty($d['area']) && is_array($match['area'])) { // Some countries can have multiple country areas // the target address can match on (most specific matches first) if (in_array($rule['area'], $match['area'])) { unset($d['area']); } // Clear excpetion to match } // Handle postcode matching if (!empty($d['postcode'])) { if (false !== strpos($rule['postcode'], ',')) { $postcodes = explode(',', $rule['postcode']); } else { $postcodes = array($rule['postcode']); } //Exclusive rules need to be evaluated first usort($postcodes, create_function('$a, $b', '$a = ( "!" == $a{0} ); $b = ( "!" == $b{0} ); if ( $a == $b ) return 0; return ( $a < $b ) ? -1 : 1;')); $exclusions = 0; foreach ($postcodes as $coderule) { $coderule = trim($coderule); // Determine if rule is exclusive $exclude = false; if ('!' == substr($coderule, 0, 1)) { $exclude = true; $exclusions++; $coderule = substr($coderule, 1); } // Match numeric postcode ranges (only works for pure numeric postcodes like US zip codes) // Cannot be mixed with wildcard ranges (eg 55*-56* does not work, use 55000-56999) if (false !== strpos($coderule, '-')) { list($start, $end) = explode('-', $coderule); if ($match['postcode'] >= $start && $match['postcode'] <= $end) { if ($exclude) { return false; } unset($d['postcode']); // Clear exception to match } continue; } // Match wildcard postcode patterns if (false !== strpos($coderule, '*')) { $pattern = str_replace('*', '(.+?)', $coderule); if (preg_match("/^{$pattern}\$/i", $match['postcode'])) { if ($exclude) { return false; } unset($d['postcode']); // Clear exception to match } continue; } // Exact match if ($coderule == $match['postcode']) { if ($exclude) { return false; } unset($d['postcode']); // Clear exception to match continue; } } if ($exclusions == count($postcodes)) { unset($d['postcode']); } //All of the rules were exclusive and passed, clear exception } // If exceptions were cleared, return the matching rate if (empty($d)) { return $r; } } // No matches found!? return false; }