4

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.

9
  • You can just type cast to float (float) before number_format. Commented Nov 29, 2016 at 13:25
  • Hi @Andrew, please see 3rd examples before/after locale setting. It replaces the dot with a comma. Difference with 2nd example is the (float) cast. Commented Nov 29, 2016 at 13:26
  • What is $client? Is this some class you can change? If this client works with your locale settings, it might always change this to use comma as seperator, which results in a string. And by the way: number_format always returns a string. Commented Nov 29, 2016 at 13:29
  • Hi @Seb. $client is Wienkit's BeslistShopItemClient. I've debugged through the functions until where the call is made, the array ($updateArray) does not get touched or modified before json_encode by the cURL library. Which results in the last example (extra json_encode() test). Commented Nov 29, 2016 at 13:31
  • Have you tried setting locale to en_US before initializing the client, and reset it after the call? Commented Nov 29, 2016 at 13:35

1 Answer 1

2

@Nukeface's issue was related to the locale setting and different variables passed to setlocale() based on platform; specifically that Windows uses different locale names from unix systems.

setlocale's return value can help you diagnose if the call worked at all, and you can use localeconv() to see some of your current locale settings.

It's likely you want to only do this specific to json_encode()'d information, so consider wrapping a setlocale to the right locale, the encode, and a setlocale back to your standard in its own function.

Here are the old answers that led to the discussion in the comments:


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]
Sign up to request clarification or add additional context in comments.

8 Comments

Just been mucking around with your answer, but when I copy you second $a[] (with the float), I get: 17,95 (not a string, but with a comma)
Try passing your desired separators in to number_format as follows: $a[] = ((float)number_format(str_replace(',', '.', $price), 2, '.', ','));
If this doesn't work, there's likely some forced conversion for your number formats with the locale settings. Try setting your LC_NUMERIC locale to 'en_US.utf8' before using json_encode. You may want to switch it back to the default after encoding to maintain state. You can verify your delimiter at this stage using localeconv()'s entry for decimal_point
Have just tried: setlocale(LC_ALL, 'en_US.utf8') and setlocale(LC_NUMERIC, 'en_US.utf8). These were followed immediately (next line of code) by your suggestion. Both return: 17,95. Checking localeconv() both times it showed: decimal_point => ',' (comma). Any suggestion as to what might cause this? Would most likely be on our live server for this project as well, so important to have that figured out.
Check out this post: stackoverflow.com/questions/10909911/… They are going the oposite way but it might be helpful to figure out, if any of the given ideas work. At least it looks like your setlocale is not working.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.