From ac46d3ef1be863cc68793ee8786784a382b199cc Mon Sep 17 00:00:00 2001 From: Ronald A Richardson Date: Wed, 15 Apr 2026 03:44:46 -0400 Subject: [PATCH 1/2] fix: resolve currency code correctly for both list and map shapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PragmaRX Countries package returns the 'currencies' field in two distinct shapes depending on the underlying data source: 1. A sequential list of ISO 4217 code strings, e.g. ["USD", "EUR"] 2. An associative map keyed by ISO 4217 code with detail objects as values, e.g. {"USD": {"name": "United States dollar", "symbol": "$"}} The previous code used Arr::first() unconditionally. For the list shape this works correctly — Arr::first() returns the first element, which is the code string. For the associative map shape, however, Arr::first() returns the first *value* (an array), not the first *key* (the code string). This caused a TypeError in getCurrenyFromCountryCode() because the function's return type is declared as ?string but an array was returned instead. Affected territories (cca2): BQ, CC, CX, GP, GF, MQ, YT, RE, SJ, TK. Changes: - Introduce Utils::resolveCurrencyCode() which normalises both shapes: * Sequential list → Arr::first() on values (existing behaviour) * Associative map → array_key_first() to extract the code from keys Coollection / Collection objects are converted via toArray() first so that array_is_list() and array_key_first() work reliably. - Replace Arr::first(...currencies...) with resolveCurrencyCode() in getCountryData() (line 1202) so the cached country data always stores a string currency code, never an array. - Apply the same fix in getCountryCodeByCurrency() (line 1131) which used the dot-notation shorthand currencies.0 — also broken for the associative map shape. - Guard the strtolower() comparison in getCountryCodeByCurrency() with is_string() to prevent the mb_strtolower(null) deprecation warning that surfaced when currency resolved to null for map-shape countries. Fixes: TypeError: getCurrenyFromCountryCode(): Return value must be of type ?string, array returned (Utils.php:1235) --- src/Support/Utils.php | 53 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/Support/Utils.php b/src/Support/Utils.php index e45a6b31..1e011885 100644 --- a/src/Support/Utils.php +++ b/src/Support/Utils.php @@ -1128,7 +1128,7 @@ public static function getCountryCodeByCurrency(?string $currencyCode, ?string $ return [ 'name' => static::get($country, 'name.common'), 'iso2' => static::get($country, 'cca2'), - 'currency' => static::get($country, 'currencies.0'), + 'currency' => static::resolveCurrencyCode(static::get($country, 'currencies', [])), ]; }) ->values() @@ -1136,7 +1136,7 @@ public static function getCountryCodeByCurrency(?string $currencyCode, ?string $ $countries = collect($countries); $data = $countries->first(function ($country) use ($currencyCode) { - return strtolower($country['currency']) === strtolower($currencyCode); + return is_string($country['currency']) && strtolower($country['currency']) === strtolower($currencyCode); }); return static::get($data, 'iso2', $defaultValue); @@ -1199,7 +1199,7 @@ public static function getCountryData(?string $country): ?array 'aliases' => static::get($country, 'alt_spellings', []), 'capital' => static::get($country, 'capital_rinvex'), 'geo' => static::get($country, 'geo'), - 'currency' => Arr::first(static::get($country, 'currencies', [])), + 'currency' => static::resolveCurrencyCode(static::get($country, 'currencies', [])), 'dial_code' => Arr::first(static::get($country, 'calling_codes', [])), 'coordinates' => [ 'longitude' => $longitude, @@ -1217,6 +1217,53 @@ public static function getCountryData(?string $country): ?array return $data ?? null; } + /** + * Resolve a currency code string from a currencies value returned by the PragmaRX Countries package. + * + * The package returns currencies in two distinct shapes depending on the data source: + * - A sequential list of ISO 4217 code strings, e.g. ["USD", "EUR"] + * - An associative map keyed by ISO 4217 code, e.g. {"USD": {"name": "...", "symbol": "..."}} + * + * When the value is a list, Arr::first() correctly returns the first code string. + * When the value is a map, Arr::first() returns the first *value* (an array), not the key. + * This method normalises both shapes and always returns the first currency code as a string, + * or null when no currencies are present. + * + * @param mixed $currencies the raw currencies value from the countries package + * + * @return string|null the first ISO 4217 currency code, or null if unavailable + */ + public static function resolveCurrencyCode($currencies): ?string + { + if (empty($currencies)) { + return null; + } + + // Convert Coollection / Eloquent Collection objects to a plain array so that + // both array_is_list() and array_key_first() work reliably. + if (is_object($currencies) && method_exists($currencies, 'toArray')) { + $currencies = $currencies->toArray(); + } + + if (!is_array($currencies)) { + return null; + } + + // Sequential list shape: ["USD", "EUR", ...] + // The first element is already the ISO 4217 code string. + if (array_is_list($currencies)) { + $code = Arr::first($currencies); + + return is_string($code) ? $code : null; + } + + // Associative map shape: {"USD": {"name": "...", "symbol": "..."}, ...} + // The keys are the ISO 4217 code strings; the values are detail objects. + $code = array_key_first($currencies); + + return is_string($code) ? $code : null; + } + /** * Retrieve currency from given ISO country code. * From de1ef41045e3d5be3614b4aae9e1066a419e6961 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 16 Apr 2026 10:27:02 +0900 Subject: [PATCH 2/2] bump version to v1.6.40 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 52dc9673..8e2ca05d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/core-api", - "version": "1.6.39", + "version": "1.6.40", "description": "Core Framework and Resources for Fleetbase API", "keywords": [ "fleetbase",