From dc9275909f7f4ff8407ef9d55ae4ccf01230a74c Mon Sep 17 00:00:00 2001 From: O K Date: Mon, 8 Dec 2025 13:21:40 +0200 Subject: [PATCH] added apiRuntimeCache --- usps_api_bridge.php | 84 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/usps_api_bridge.php b/usps_api_bridge.php index b19ab74..37d8627 100644 --- a/usps_api_bridge.php +++ b/usps_api_bridge.php @@ -8,6 +8,9 @@ use Symfony\Component\HttpClient\HttpClient; class Usps_Api_Bridge extends Module { + // Runtime cache to prevent duplicate API calls during a single page load + private $apiRuntimeCache = []; + public function __construct() { $this->name = 'usps_api_bridge'; @@ -139,7 +142,7 @@ class Usps_Api_Bridge extends Module return $helper->generateForm([$fields_form]); } -public function calculateRate($params, $shipping_cost, $products, $originalModule) + public function calculateRate($params, $shipping_cost, $products, $originalModule) { require_once(dirname(__FILE__) . '/classes/UspsV3Client.php'); @@ -155,7 +158,7 @@ public function calculateRate($params, $shipping_cost, $products, $originalModul $methodCode = Db::getInstance()->getValue($sql); if (!$methodCode) { - return false; + return false; } // 3. Map Old Code to New API Enum @@ -166,7 +169,7 @@ public function calculateRate($params, $shipping_cost, $products, $originalModul // 4. Pack Products $packedBoxes = $originalModule->getHelper()->getCarrierHelper()->packProducts($products, $params->id); - + if (empty($packedBoxes)) { $this->log("Box packer returned empty."); return false; @@ -184,20 +187,20 @@ public function calculateRate($params, $shipping_cost, $products, $originalModul // 6. Address Data $originZip = $this->getOriginZip($originalModule); $destAddress = new Address($params->id_address_delivery); - + $originZip = substr(preg_replace('/[^0-9]/', '', $originZip), 0, 5); $destZip = substr(preg_replace('/[^0-9]/', '', $destAddress->postcode), 0, 5); $isInternational = ($destAddress->id_country != Country::getByIso('US')); // 7. Loop through boxes foreach ($packedBoxes as $packedBox) { - + // Weight (Lbs) - Ensure minimum 0.1 $weightInLbs = $this->convertUnit($packedBox->getWeight(), 'g', 'lbs', 3); if ($weightInLbs < 0.1) $weightInLbs = 0.1; // Dimensions (Inches) - $box = $packedBox->getBox(); + $box = $packedBox->getBox(); $length = $this->convertUnit($box->getOuterLength(), 'mm', 'in', 2); $width = $this->convertUnit($box->getOuterWidth(), 'mm', 'in', 2); $height = $this->convertUnit($box->getOuterDepth(), 'mm', 'in', 2); @@ -259,24 +262,65 @@ public function calculateRate($params, $shipping_cost, $products, $originalModul } /** - * Helper to send request and handle Domestic vs International switching + * Helper to send request with Runtime Caching & Domestic/Intl switching */ private function sendApiRequest($client, $payload, $isInternational, $destAddress, $destZip) { + // 1. Prepare the specific payload for the cache key + // We simulate the modifications we are about to do to ensure the key is accurate + $cachePayload = $payload; + if ($isInternational) { - $payload['destinationCountryCode'] = Country::getIsoById($destAddress->id_country); - // Cleanup domestic fields - unset($payload['destinationEntryFacilityType']); - unset($payload['destinationZIPCode']); - - return $client->getInternationalRate($payload); - } - - // Domestic - $payload['destinationZIPCode'] = $destZip; - $payload['destinationEntryFacilityType'] = 'NONE'; - - return $client->getDomesticRate($payload); + $cachePayload['destinationCountryCode'] = Country::getIsoById($destAddress->id_country); + $cachePayload['originZIPCode'] = $payload['originZIPCode']; // Ensure consistency + unset($cachePayload['destinationEntryFacilityType']); + unset($cachePayload['destinationZIPCode']); + $endpointType = 'INT'; + } else { + $cachePayload['destinationZIPCode'] = $destZip; + $cachePayload['destinationEntryFacilityType'] = 'NONE'; + $endpointType = 'DOM'; + } + + // 2. Generate Hash + // We include the endpoint type to ensure uniqueness + $cacheKey = md5(json_encode($cachePayload) . $endpointType); + + // 3. Check Cache + if (isset($this->apiRuntimeCache[$cacheKey])) { + // Uncomment for deep debugging if needed + // $this->log("Returning cached rate for key: " . $cacheKey); + return $this->apiRuntimeCache[$cacheKey]; + } + + // 4. Perform Request + if ($isInternational) { + $response = $client->getInternationalRate($cachePayload); + } else { + $response = $client->getDomesticRate($cachePayload); + } + + // 5. Determine if we should cache + // We DO cache API errors (like 400 Bad Request) because retrying them won't fix invalid data. + // We DO NOT cache Network/Transport errors (timeouts) so they can be retried. + $shouldCache = true; + + if (isset($response['error'])) { + // Check for Guzzle/Symfony Transport errors strings defined in UspsV3Client + if ( + strpos($response['error'], 'Network') !== false || + strpos($response['error'], 'Connection') !== false || + strpos($response['error'], 'Transport') !== false + ) { + $shouldCache = false; + } + } + + if ($shouldCache) { + $this->apiRuntimeCache[$cacheKey] = $response; + } + + return $response; } /**