==
!=
, >
, >=
, <
, <=
and <=>
with non-numeric operands will throw a TypeError
..
) on null
does not throw a TypeError
, but will cast null
to an empty string.switch
is not affected by this directive.Making significant changes to the behavior of operators has severe consequences to backward compatibility. Additionally, there is a significant group of people who are in favor of the current method of type juggling. Following the rationale of PHP RFC: Scalar Type Declarations; an optional directive ensures backward compatibility and allows people to choose the type checking model that suits them best.
The main difference between the equal (==
) and identical (===
) operator, is that ==
doesn't perform type casting. By only disabling type casting with strict_operators, ==
would be similar to ===
for scalar types.
This RFC tries to avoid behaviorial changes based on the strict_operators directives.
The ==
and !=
operators compares two arrays as unsorted hashmaps. This is a useful feature. However, it's currently tied in with type juggling. Comparing the values of the array with type checking, similar to ===
, would significatly change the behavior of an operation based on the strict_operators directive. Example; ['a' => null] == ['a' => 0]
would result in true
when the strict_operators is enabled and false
otherwhise.
The ==
and !=
operators compares two objects by property. This is tied in with type juggling. Similar to arrays, comparing property values with ===
would change the behavior of the ==
operation.
Comparing two numeric strings using ==
and !=
will compare them as numbers. Disabling type juggling would change this behavior and make these operators the same as ===
and !===
in this case.
In many other languages, using ==
, !=
, <
, >
, <=
and =>
with string operands performs an strcmp
like operation. The <=>
is even described as strcmp as operators. Why do these operators throw an exception with string operands when strict_operators is enabled?
It's common to use these operators when both operands are numeric strings. Having these statements return a different value based on the directive, could lead to issues, especially when source code is copy/pasted from an external codebase.
In case it concerns comparing text, it's better to use Collator::compare()
. For non-collation related strings, like date strings, the strcmp
function should be used.
Nikita Popov made the following argument:
Having$str1 < $str2
perform astrcmp()
style comparison under strict_operators is surprising. I think that overall the use of lexicographical string comparisons is quite rare and should be performed using an explicitstrcmp()
call. More likely than not, writing$str1 < $str2
is a bug and should generate aTypeError
. Of course, equality comparisons like$str1 == $str2
should still work, similar to the distinction you make for arrays.
The concatenation operator will cast integers, floats and (when __toString
is defined) objects to a string. This is a common use case and operands are always typecasted to a string. Integers and floats always have a proper string representation.
Arithmetic operators won't cast strings to an integer or float, because not all strings can be properly represented as a number and a TypeError
must be thrown based on the operand type only, not the value.
Both the concatenation operator and arithmetic operators throw a TypeError
for arrays, resources, and objects. Casting these to a string or int/float doesn't give a proper representation of the value of the operand.
Using a boolean or null as operand for both concatenation and arithmetic operators also throws a TypeError
. In most cases, the use of a boolean or null indicates an error as many functions return false
or null
in case of an error or when no result can be returned. This is different from the function returning an empty string or 0
. strpos is a well-known example.
No, this will throw a TypeError
. Users that use string operators need to explicitly typecast the string to an integer. If it concerns input data, for instance from $_POST
, it's recommended to use the filter functions.
Arrays are sorted hashmaps in PHP but used as any type of collection like unsorted maps, lists, and sets. Only comparing as unsorted hashmap is supported using ==
, while other cases require the use of functions like sort()
and array_values()
.
Strict comparison of arrays as unsorted hashmaps currently isn't possible and requires sorting the array, prior to comparison.
With ==
unavailable when using strict_operators, sorting the array is the only option available.
ksort($array1); ksort($array2); $array1 === $array2;
Array functions might be added to compare arrays in different ways. But that's outside the scope of this RFC.
With the ==
operator, two object instances are equal if they have the same attributes and values, and are instances of the same class. Values are compared with ==
, applying type juggling. Using ===
will check if the objects are the same instance.
When using strict_operators, ==
with objects will always throw a TypeError
. With ==
unavailable, get_object_vars
needs to be used.
get_class($object1) === get_class($object2) && get_object_vars($object1) === get_object_vars($object2);
Nikita Popov made the following argument:
String increment seems like a pretty niche use case, and I believe that many people find the overflow behavior quite surprising. I think it may be better to forbid string increment under strict_operators.
No. Only operators (including the case
statement) in the source file that has declare(strict_operators=1)
are affected. Functions defined elsewhere, including functions defined in extensions, are not affected.
Specifically sort()
and in_array()
will apply type jugging by default and require the use of the $sort_flags
and $strict
arguments to change the way the values are compared.
If both arrays must have the same keys in the same order, using <
or >
on two arrays can be useful. But, as shown in the examples, when this is not the case, these type of comparisons will yield unexpected results.
Throwing a TypeError
only if the keys of the arrays don't match is not in line with this RFC. The behavior of an operator should not depend on the value of the operand, only on the type. Furthermore, a TypeError
would be misplaced here, as some arrays would be accepted but others not, whereas a TypeError
indicates no values of that type are accepted.
Using strict_operators will only result in cases where a TypeError
is thrown. It will not affect the behavior, giving a different result.
Changing switch
to compare using ===
would change the behavior based on the directive.
To use strict comparison, most switch
statements can be rewritten to match statements.
No.
No. Operators can still typecast under the given conditions. For instance, the concatenation (.
) operator will cast an integer or float to a string, and boolean operators will cast any type of operand to a boolean.
Type juggling is also done in other places: if
and while
statements interpret expressions as boolean, and booleans and floats are cast to an integer when used as array keys. The switch
statement will still use loose comparison (==
).
This RFC is limited to the scope of operators.