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.

Community
  • 1
  • 1
rkeet
  • 3,406
  • 2
  • 23
  • 49
  • You can just type cast to float `(float)` before `number_format`. – Andrei Nov 29 '16 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. – rkeet Nov 29 '16 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. – Seb Nov 29 '16 at 13:29
  • Hi @Seb. `$client` is Wienkit's [BeslistShopItemClient](https://github.com/wienkit/beslist-shopitem-api-php-client). 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). – rkeet Nov 29 '16 at 13:31
  • Have you tried setting locale to en_US before initializing the client, and reset it after the call? – Seb Nov 29 '16 at 13:35
  • Bad wording on my part. I mean cast to float the `$price` itself, not the results of number format. – Andrei Nov 29 '16 at 13:37
  • @Seb Yes I have, tried some more variations based on [answer below](http://stackoverflow.com/a/40867300/1155833) from Kevin. No solution as of yet though. – rkeet Nov 29 '16 at 14:14
  • @Andrew doing `(float) $price` where `$price = "17,95"` results in: `17`. – rkeet Nov 29 '16 at 14:15
  • You can also use a cascading locale to find out which is the preferred locale on the current system. `$locale = setlocale(LC_ALL, 'nl_NL', 'nl');` or `$locale = setlocale(LC_ALL, 'en_US.utf8', 'en_US', 'us', 'en');` and see which one was set `echo $locale;` – Will B. Nov 29 '16 at 15:33

1 Answers1

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]
Kevin Stich
  • 773
  • 1
  • 8
  • 24
  • 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) – rkeet Nov 29 '16 at 13:43
  • Try passing your desired separators in to number_format as follows: `$a[] = ((float)number_format(str_replace(',', '.', $price), 2, '.', ','));` – Kevin Stich Nov 29 '16 at 14:00
  • 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()`](http://php.net/manual/en/function.localeconv.php)'s entry for `decimal_point` – Kevin Stich Nov 29 '16 at 14:04
  • 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. – rkeet Nov 29 '16 at 14:13
  • 1
    Check out this post: http://stackoverflow.com/questions/10909911/php-setlocale-has-no-effect 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. – Seb Nov 29 '16 at 14:19
  • What is the return value of your LC_NUMERIC setlocale call? – Kevin Stich Nov 29 '16 at 14:20
  • @KevinStich *fist-bump* That was it! Some quick searching, it's Windows that's the problem when trying to change the locale (found [here](http://stackoverflow.com/questions/10995953/php-setlocale-in-windows-7) and [here](http://stackoverflow.com/questions/9489339/setlocalelc-all-en-gb-utf8-not-working-on-windows). Found Windows supported characters [here](https://msdn.microsoft.com/en-us/library/39cwe7zf(v=vs.100).aspx). `setlocale(LC_ALL, 'us')` actually sets to US English (instead of `en_US`). I'll update question with answer+ref to your comment here! – rkeet Nov 29 '16 at 14:31
  • Great to hear! I'll update my answer shortly with info from these comments. You should consider setting up a new function to set your locales to and from the US around your json_encode calls. – Kevin Stich Nov 29 '16 at 14:53