Description
Symfony version(s) affected: I'm using 2.8.34
(but the problematic code is also in the master branch, so all versions should be affected)
Description
In PHP, when converting a float value to string, the character used as a decimal-seperator depends on the locale. In an english locale, the full-stop "." is used ("123.45") while in german (f.e.) the comma "," is used ("123,45"). The PHP internal function "is_numeric" does not recognize the comma and would only consider the first representation ("123.45") as a number, but not the second one:
php > setlocale(LC_ALL, "de_DE.UTF-8");
php > var_dump(12 / 34);
float(0,35294117647059)
php > setlocale(LC_ALL, "en_US.UTF-8");
php > var_dump(12 / 34);
float(0.35294117647059)
php > var_dump(is_numeric("0.35294117647059"));
bool(true)
php > var_dump(is_numeric("0,35294117647059"));
bool(false)
php > setlocale(LC_ALL, "de_DE.UTF-8");
php > var_dump(is_numeric((string)(12 / 34)));
bool(false)
php > setlocale(LC_ALL, "en_US.UTF-8");
php > var_dump(is_numeric((string)(12 / 34)));
bool(true)
There is at least one piece of code in symfony where this behaviour leads to code breaking in certain locales and not in others. There are probably more but i have not checked yet. I have just stumbled over one such code in the MoneyToLocalizedStringTransformer on line 61 and NumberToLocalizedStringTransformer on line 113:
public function transform($value)
{
if (null !== $value && 1 !== $this->divisor) {
...
$value = (string) ($value / $this->divisor);
}
return parent::transform($value);
}
In an english locale, $value contains something like "123.45", which passes the is_number check.
In an german locale, $value contains something like "123,45", which fails the is_number check.
The call "parent::transform($value)" from above directly calls the code below:
public function transform($value)
{
...
if (!is_numeric($value)) {
throw new TransformationFailedException('Expected a numeric.');
}
...
This piece of code will now fail in a german locale and pass in an english one.
How to reproduce
Switch to a german locale ("de_DE.UTF-8") and use the Money transformers. I do with the Tbbc\MoneyBundle, but the problematic code is in symfony (see above) and not that bundle.
Possible Solution
I don't know if that is the best solution, but a simple "$value = str_replace(',', '.', $value);" in the MoneyToLocalizedStringTransformer directly after calculating and string-casting the $value should do the trick at least for me; I'm not sure about other locales. Maybe more research is required here for a better solution. This works for me:
public function transform($value)
{
if (null !== $value && 1 !== $this->divisor) {
...
$value = (string) ($value / $this->divisor);
$value = str_replace(",", ".", $value);
}
return parent::transform($value);
}