Nukeface Nukeface - 7 days ago 5
PHP Question

How to get numeric float (not string!) when json_encoding?

I need to have a numeric float using a dot separator, that stays a numeric after (json) encoding for sending in the POST headers to a third-party API.

However, have been trying for a few hours, but cannot get it to work.

What I need is:

{
"price": 17.95,
// BUT NOT:
"price": "17.95" OR 17,95
}


Why? Because the receiving API endpoint does a check for the first but throws a
non-numeric
error on the latter 2 values.

Now, we're in Holland. So our "locale" uses a comma separator. Working around that by setting the locale from
nl_NL
to
en_US
gives the
number_format
function the correct format, however, as a string.

Casting a comma or dot separated string using
(float)
causes it to lose any value from the point of separation. (
"17.95"
becomes
17
)

Updating the product details is a function taking a few arguments that passes them on without modification to cURL. Which encodes the array of POST variables into what it should be above. I'm limited in passing the following:

$client->updateShopItem($shopId, $articleNumber, $updateArray)


$shopId
= int
$articleNumber
= string
$updateArray
= array

Complete, correct, call looks like:

$client->updateShopItem(12345, "a1b2c3", [
"price" => 17.95,
"sale_price" => 12.99,
//... other values
]);


Values to use instead of the ones in example above are string type: "17,95".

Have tried:

$price = "17,95" //Starting point (locale = nl_NL)

number_format($price, 2) // "17.00" - incorrect type and value
number_format(str_replace(',', '.', $price), 2) // "17.95" - string
(float) str_replace(',', '.', $price); // 17,95 - comma
(float) number_format(str_replace(',', '.', $price), 2) //17,95 - comma

setLocale(LC_ALL, 'en_US'); //Changing locale here, US uses dot separator

$check = locale_get_default(); // "en_US"

number_format($price, 2) // "17.00" - incorrect type and value
number_format(str_replace(',', '.', $price), 2) // "17.95" - string
(float) number_format(str_replace(',', '.', $price), 2) // 17,95 - comma
(float) str_replace(',', '.', $price); // 17,95 (can't figure why comma with US locale)


Extra test to see what
json_encode()
makes of the different types/values:

json_encode(["test1" => "17,95", "test2" => 17.95]);

//Results in:
{"test1":"17,95","test2":17.95}


UPDATE: to avoid confusion: full code, only removed non-relevant stuff, otherwise unedited. Has included locale se

setlocale(LC_ALL, 'nl_NL');
ini_set('intl.default_locale', 'nl-NL');
$update = [
'price' => ((float)number_format(str_replace(',', '.', $prijs), 2)),
'discount_price' => (float) str_replace(',', '.', $actieprijs),
];

setlocale(LC_ALL, 'en_US');
ini_set('intl.default_locale', 'en-US');
// Update a shopitem
$update2 = [
'price' => ((float)number_format(str_replace(',', '.', $prijs), 2)),
'discount_price' => (float) str_replace(',', '.', $actieprijs),
];


enter image description here

UPDATE 2: SOLUTION FOUND
After some comments back 'n' forth with @KevinStich about his answer found that the problem when trying to change the locale was in the fact that I'm on Windows.

His answer solves it, after adding the following code above where it needs to get set:

if (!setlocale(LC_ALL, 'en_US.utf8')) { //Works on "normal" server/Linux stuff
setlocale(LC_ALL, 'us'); //Windows is special
}


Found in the docs that the
setlocale()
function returns
false
if it didn't set a new locale, or else returns the new locale. Which led to the above and that @KevinStich's answer worked.

Answer

It looks like there's something odd going on with the scoping of your cast. I've seen this issue before and am not sure what to chalk it up to, I solved it with more parenthesis.

This worked for me in the REPL

var_dump(
    ((float)number_format(str_replace(',', '.', $price), 2))
);
float(17.95)

To make sure it's not a json_encode issue:

$price = "17,95";
$a = array();
$a[] = $price;
$a[] = ((float)number_format(str_replace(',', '.', $price), 2));

echo json_encode($a); // Prints ["17,95",17.95]