Unexpected behavior of the ‘in’ operator
What
I expect false to return, but true returns.
How to reproduce
tested with twig/twig 3.21.1
{% set fooBar = {0: 'foo', 1: 'bar'} %}
{{ dump( true in fooBar, false in fooBar, 0 in fooBar, 1 in fooBar ) }}
Will return: true , false, false, false
I expect to return false in all cases, but the first case returns true.
I agree, looking at the code, it seems to me that the "in" operator is just not made to handle boolean values properly. As it is now, it is confusing. IMO this should be fixed or throw an exception when called with a boolean left operand instead of producing unexpected results.
As there's no special handling like is_bool($value) I think we end up here: https://github.com/twigphp/Twig/blob/3.x/src/Extension/CoreExtension.php#L1099 and then here: https://github.com/twigphp/Twig/blob/3.x/src/Extension/CoreExtension.php#L1166
php > var_dump(true <=> 'foo');
int(0)
php > var_dump(false <=> 'foo');
int(-1)
https://github.com/twigphp/Twig/blob/3.x/src/Extension/CoreExtension.php#L1084 I think changing:
if (\is_object($value) || \is_resource($value)) {
to:
if (\is_object($value) || \is_resource($value) || \is_bool($value)) {
would do it.
It seems that Twig is matching the default behavior of PHP when using in_array. The behavior you are expecting would be the case when passing true as the second argument to in_array in order to do strict comparisons. You can see how PHP handles this here.
@willrowe
maybe, but the twig syntax (which has no equivalent in php) also returns unexpected true:
{{ dump( true in 'a'..'z' ) }} will returns true
@gorenstein I'm saying that in_array would be the PHP equivalent and that true is not unexpected since it's a loose comparison.
Similarly, using == in Twig is a loose comparison just like it is in PHP. is same as() exists in order to explicitly allow strict comparisons . Generally, the default in Twig is loose comparisons, just like PHP.
The compare method referenced by @edditor has this in the docblock:
Compares two values using a more strict version of the PHP non-strict comparison operator.
If this is not a bug but a feature, then at least the documentation should be improved. Because right now it says:
https://twig.symfony.com/doc/3.x/templates.html#containment-operators
But in any case, as it is implemented now, it is misleading and error provoking. Since depending on the type of the left|right operands, the comparison logic changes (strict and non-strict).
{% set fooBar = 'true, false, 0, 1' %}
{{ dump( true in fooBar, false in fooBar, 0 in fooBar, 1 in fooBar ) }}
will return: false false true true
It is absolutely clear that with this implementation, “in” operator of Boolean types are handled differently than other types.