rfc:strict_operators

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
rfc:strict_operators [2019/07/15 07:02]
jasny Forgot to mark the version as 1.0 on voting last time
rfc:strict_operators [2020/07/06 21:31]
jasny
Line 1: Line 1:
 ====== PHP RFC: Strict operators directive ====== ====== PHP RFC: Strict operators directive ======
-  * Version: 1.0 + 
-  * Date: 2019-05-25 +  * Version: 1.5 
-  * Author: Arnold Daniels, jasny@php.net+  * Date: 2020-07-06 (first version: 2019-05-25) 
 +  * Author: Arnold Daniels, <jasny@php.net>
   * Status: Under Discussion   * Status: Under Discussion
   * First Published at: http://wiki.php.net/rfc/strict_operators   * First Published at: http://wiki.php.net/rfc/strict_operators
- 
  
 ===== Introduction ===== ===== Introduction =====
-PHP performs implicit type conversion for most operators. The rules of conversion are complex, depending on the operator as well as on the type and value of the operands. This can lead to surprising results, where a statement seemingly contradicts itself. This RFC proposes a new directive ''strict_operators'', which limits the type juggling done by operators and makes them throw a ''TypeError'' for unsupported types. 
  
-Making significant changes to the behavior of operators has significant consequences to backward compatibility. Additionallythere is a significant group of people who are in favor of the current method of type juggling. Following the rationale of [[rfc:scalar_type_hints_v5|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 rules PHP uses for type juggling with operators are complexvarying by operator as well as the types and values of the operands.
  
-===== Motivating examples =====+This can lead to surprising results, where a logical statement can have an illogical result. See the "Motivating examples" section below for details.
  
-==== Mixed type comparison ==== +This RFC proposes new directive ''%%strict_operators%%'', which limits the type juggling done by operators to avoid unexpected results.
-Mathematics states that "if ''(> b)'' and ''(b > c)'', then ''(a > c)''"This statement can be asserted in PHP;+
  
-<code php> +===== Proposal =====
-if (($a > $b) && ($b > $c)) { +
-    assert($a > $c); +
-+
-</code>+
  
-This assertion fails when choosing values of different types+Add a new ''%%declare()%%'' directive ''%%strict_operators%%'', which accepts either ''%%0%%'' or ''%%1%%'' as the value to indicate that the strict_operators mode should be disabled or enabled.
  
-<code php> +The default value for this directive is 0, i.e. strict_operators not enabled and there will be no change from the current behavior of PHP i.e. by default, PHP files do not use the new strict_operators rules.
-$a = '42'; +
-$b = 10; +
-$c = '9 eur';+
  
-if (($a > $b) && ($b > $c)) { +If strict_operators is enabled, the following stricter rules will be used;
-    assert($a > $c); +
-+
-</code>+
  
-==== Numeric string comparison ==== +  * Operators may perform type conversion, but not type juggling: 
-Non-strict comparison uses a "smart" comparison method that treats strings as numbers if they are numeric. The meaning of the operator changes based on the value of both operands.+    * Type conversion is not based on the type of any of the operands 
 +    * Type conversion is not based on the value of any of the operands 
 +  * Operators will throw a ''%%TypeError%%'' for unsupported types
  
-Using the ''%%<=>%%'' operator to order the values of an array can lead to different results based on the initial state of the array.+In case an operator can work with several (or all) types, the operand types need to match as no type conversion will be done by those operators.
  
-<code php> +The one exception is that [[http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2|widening primitive conversion]] is allowed for ''%%int%%'' to ''%%float%%''. When doing a operation with an ''%%int%%'' and a ''%%float%%''the ''%%int%%'' will silently casted to a ''%%float%%''.
-function sorted(array $arr) { +
-  usort($arrfunction($x, $y) { return $x <=> $y; }); +
-  return $arr; +
-}+
  
-sorted(['100''5 eur', '62']); // ['100', '5 eur', '62'+Using ''strict_operators'' never changes the outcome of an operation.
-sorted(['100', '62', '5 eur']); // ['5 eur', '62', '100'+
-sorted(['62', '100', '5 eur']); // ['62', '100', '5 eur'+
-</code>+
  
-==== Array comparison ==== +===== Details for operands =====
-Using the ''>'', ''>='', ''<'', ''%%<=%%'' and ''%%<=>%%'' operators on arrays or objects that don't have the same keys in the same order gives unexpected results.+
  
-In the following example ''$a'' is both greater than and less than ''$b''+This section details which types operands support, and what type juggling will be supported (if any) for them.
  
-<code php> +==== Arithmetic operators ====
-$a ['x' => 1, 'y' => 22]; +
-$b ['y' => 10, 'x' => 15];+
  
-$a > $b; // true +Arithmetic operators ''%%+%%'', ''%%-%%'', ''%%*%%'', ''%%/%%'', ''%%%%%'', and ''%%**%%'' will only support ''%%int%%'' or ''%%float%%'' operands. Attempting to use an unsupported operand will throw ''%%TypeError%%''.
-$< $b; // true +
-</code>+
  
-The logic of relational operators other than ''=='', ''==='', ''!='' and ''!=='' has limited practical use. In case both arrays have the same keys (in the same order), a side-by-side comparison is doneIf the size differs, the array with the most elements is always seen as the greatest;+The ''%%+%%'' operator will still be available for arrays as union operatorrequiring both values to be arrays.
  
-<code php> +==== Incrementing/Decrementing operators ====
-[1] < [50]; // true +
-[1, 1] < [50]; // false +
-</code>+
  
-This is not a proper method to compare the size of the array, as two operands of equal size but different values are not equalInstead, ''count()'' should be used in this case.+The incrementing/decrementing operators ''%%++%%'' and ''%%--%%'' will only support ''%%int%%'' or ''%%float%%'' operands. Attempting to use an unsupported operand will throw a ''%%TypeError%%''.
  
-If two arrays have the same number of items but not the same keys, the ''<'', ''%%<=%%'', ''>'' and ''>='' operators will always return false.+==== Bitwise Operators ====
  
-<code php> +The bitwise operators ''%%&%%'', ''%%|%%'', ''%%^%%'', and ''%%~%%'' will support ''%%int%%'' and ''%%string%%'' operands . The type of both operands need to match. If the operands are of different types or when using an unsupported operand, a ''%%TypeError%%'' will be thrown.
-[1] < ['bar=> 50]; // false +
-[1] > ['bar=> 50]; // false +
-</code>+
  
-In case the two arrays have the same number of items and the same keys but in a different order, an element by element comparison is done. The ''>'' and ''>='' operator is implemented as the inverse of ''<'' and ''%%<=%%''This results in walking through the operand that's expected to be the smallest.+The bitwise shift operators ''%%>>%%'' and ''%%<<%%'' will only support ''%%int%%'' operandsAttempting to use an unsupported operand will throw a ''%%TypeError%%''.
  
-<code php> +==== Comparison operators ====
-$a ['x' => 1, 'y' => 22]; +
-$b ['y' => 10, 'x' => 15];+
  
-$a > $b; // true +Enabling the strict_operators directive will not affect the identical (''%%===%%'') and not identical (''%%!==%%'') operators.
-$a < $b; // true +
-</code>+
  
-In the statement with the ''>'' operatorwe walk through the elements of ''$b'', so first comparing ''$b['y']'' to ''$a['y']''. In the statement with ''<'' we walk through the elements of ''$a'', so first comparing ''$a['x']'' to ''$b['x']''. This results in both statements, while seemingly contracting, to evaluate to true.+All other comparison operators (''%%==%%'', ''%%!=%%'', ''%%<%%''''%%>%%''''%%<=%%''''%%>=%%'', ''%%<=>%%'') will only support ''%%int%%'' or ''%%float%%'' operands.
  
-==== Strict vs non-strict comparison of arrays ==== +When used with a ''%%bool%%''''%%string%%'', ''%%array%%'', ''%%object%%'', or ''%%resource%%'' operand, a ''%%TypeError%%'' will be thrown.
-Strict comparison requires that arrays have keys occurring in the same orderwhile non-strict comparison allows out-of-order keys.+
  
-<code php> +Custom compare handlers of objects (like ''DateTime'' and ''gmp'') will be applied regardless of ''strict_operators''. The ''ZEND_COMPARE_OBJECTS_FALLBACK'' macro will throw a ''TypeError'' if two objects are compared with different handlers.
-['a=> 'foo''b=> 'bar'] == ['b=> 'bar''a' => 0]; // true +
-</code>+
  
-To compare the values of two arrays in a strict way, while not concerned about the order requires ordering the array by key.+==== String concatenation ====
  
-==== Type juggling of arithmetic operators ==== +The concatenation operator ''%%.%%'' will only support concatenating ''%%null%%'', ''%%int%%'', ''%%float%%'', ''%%string%%'', and [[https://wiki.php.net/rfc/stringable|stringable object]] operands.
-The behavior of arithmetic operators for non-scalar types is inconsistent.+
  
-Most arithmetic operations throw an ''Error'' if one of the operands is an array. But modulo and exponentiation operations will cast (to 0 or 1) and thus succeed silently.+If any of the operands is a ''%%bool%%'', ''%%array%%'', ''%%resource%%'', or non-stringable object, a ''%%TypeError%%''will be thrown.
  
-Objects and resources are always cast to integers or floats. In case of an object, this results in a notice. For resources, this will succeed silently using the resource id as a number.+=== String interpolation ===
  
-|           ^ ''$+ 10''  ^ ''$a * 10''  ^ ''$a % 10'' +When string is specified in double quotes or with heredoc, variables are parsed within it. The string interpolation is performed with the same rules as for string concatenation.
-^ array     | error        | error        | -            | +
-^ object    | notice       | notice       | notice       | +
-^ resource  | -            | -            | -            |+
  
-==== Numeric strings and bitwise operators ==== +i.ethis code
-Bitwise operators have an alternative operation if both operands are stringsThis is regardless of the value of the strings. +
- +
-> If both operands for the &, | and ^ operators are strings, then the operation will be performed on the ASCII values of the characters that make up the strings and the result will be a string. In all other cases, both operands will be converted to integers and the result will be an integer. +
- +
-Bitwise operators are therefore the only operators that don't treat numeric strings as numbers.+
  
 <code php> <code php>
-"22& "12"; // "02" +echo "He drank some $juice juice.";
-22 & 12    // 4+
 </code> </code>
  
-==== Switch control structure ==== +has the same rules as using the string concatenation operator.
-The ''switch'' statement does a non-strict comparisonThis can lead to unexpected results;+
  
 <code php> <code php>
-function match($value) +echo "He drank some . $juice . juice.";
-+
-  switch ($value) { +
-    case 2: +
-      return "double"+
-      break; +
-    case 1: +
-      return "single"; +
-      break; +
-    case 0: +
-      return "none"; +
-      break; +
-    default: +
-      throw new Exception("Unexpected value"); +
-  } +
-+
- +
-match("foo"); // "none"+
 </code> </code>
  
-In case both the expression and the condition operands are both numeric strings, both are converted to an integer. This can be unexpected;+==== Logical Operators ====
  
-<code php> +There a no changes to the logical operators ''%%&&%%'', ''%%||%%'', ''%%!%%'', ''%%and%%'', ''%%or%%'', and ''%%xor%%''.
-function match($value) +
-+
-  switch ($value) { +
-    case "1e1": +
-      return "1e1"; +
-      break; +
-    case "10": +
-      return "10"; +
-      break; +
-    default: +
-      throw new Exception("Unexpected value"); +
-  } +
-}+
  
-match("10"); // "1e1" +==== Ternary Null Coalescing Operator ====
-</code>+
  
 +There a no changes to the ternary (''%%?:%%'') and null coalescing (''%%??%%'') operator.
  
-==== All combinations ====+===== Motivating examples =====
  
-Operators can do any of the following for unsupported operands+The following section demonstrates code that is written logical, but has illogical results.
  
-  * Cast +==== Mixed type comparison ====
-    * silent +
-    * with notice +
-    * with warning +
-    * causing a catchable error (fatal) +
-  * Notice + cast +
-  * Warning + cast +
-  * Throw Error +
-  * No operation+
  
-Please take a look at this [[https://gist.github.com/jasny/bfd711844a8876f8206ed21357e2e2da|list of all combinations of operators and operands]].+The meaning of comparison operators currently change based on the type of each operandStrings are compared as byte sequenceIf one of the operands is an integer the operator performs a numeric comparison.
  
-===== Proposal ===== +This allows for statements that defy mathematical logic and may be experienced as unexpected behavior.
-By default, all PHP files are in weak type-checking mode for operators. A new ''declare()'' directive is added, ''strict_operators'', which takes either ''1'' or ''0''. If ''1'', strict type-checking mode is used for operators in the the file. If ''0'', weak type-checking mode is used. +
- +
-In strict type-checking mode, operators may cast operands to the expected type. However: +
- +
-  * Typecasting is not based on the type of the other operand +
-  * Typecasting is not based on the value of any of the operands +
-  * Operators will throw a ''TypeError'' for unsupported types +
- +
-In case an operator can work with several (or all) types, the operands need to match as no casting will be done by those operators. +
- +
-The one exception is that [[http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2|widening primitive conversion]] is allowed for ''int'' to ''float''. This means that parameters that declare ''float'' can also accept ''int''.+
  
 <code php> <code php>
-declare(strict_operators=1);+$a '42'; 
 +$b = 10; 
 +$c = '9 eur';
  
-1.2 + 2; // float(3.2)+if (($a > $b&& ($b > $c) && ($c > $a)) { 
 +   // Unexpected  
 +}
 </code> </code>
  
-In this case, we're passing an ''int'' to a function that accepts ''float''. The parameter is converted (widened) to float.+==== Numeric string comparison ====
  
-==== Comparison operators ==== +Non-strict comparison uses a "smart" comparison method that treats strings as numbers if they are numeric. The meaning of the operator changes based on the value of both operands.
-Comparison operators work on all scalar types. The types of both values need to match.+
  
-Non-scalar types only support the ''==''''==='', ''!='' and ''!=='' operators.+This can lead to issues when numeric comparison is not expectedfor example between two hexidecimal values. The hexidecimal value is instead interpreted as number with scientific notation.
  
 <code php> <code php>
-"foo" > "bar" // true +$red = '990000'
-"foo" > 10    // TypeError("Operator type mismatch string and int for comparison")+$purple = '9900e2';
  
-"foo" == "bar"; // false +$red == $purple; // true
-"foo" == 10;    // TypeError("Operator type mismatch string and int for comparison"+
-"foo" == null;  // TypeError("Operator type mismatch string and null for comparison"+
- +
-true > false;   // true +
-true != 0;      // TypeError("Operator type mismatch bool and int for comparison"+
- +
-[10] > [];      // TypeError("Unsupported type array for comparison"+
-[10] == [];     // false+
 </code> </code>
  
-The function of the ''==='' and ''!=='' operators remains unchanged. +It may also cause issues with sorting, as the meaning of the comparison operators differers based on the operands (similar to mixed type comparison).
- +
-=== Numeric string comparison === +
-Numeric strings are compared the same way as non-numeric strings. To compare two numeric strings as numbers, they need to be cast to floats.+
  
 <code php> <code php>
-"120" > "99.9";               // false +function sorted(array $arr) { 
-(float)"120" (float)"99.9"// true+  usort($arr, function($x, $y{ return $x <=$y; }); 
 +  return $arr; 
 +}
  
-"120" <=> "99.9"            // -1+sorted(['100', '5 eur', '62']); // ['100', '5 eur', '62'
 +sorted(['100', '62', '5 eur']); // ['5 eur', '62', '100'
 +sorted(['62', '100', '5 eur']); // ['62', '100', '5 eur']
 </code> </code>
  
-=== Array comparison === +==== Array comparison ====
-Comparing two arrays will never throw a ''TypeError''.+
  
-The difference between using the ''=='' and ''==='' operator is the order of the keys of the array. This feature remains unchanged.+Using the ''%%>%%'', ''%%>=%%'', ''%%<%%'', ''%%<=%%'' and ''%%<=>%%'' operators on arrays or objects that don't have the same keys in the same order gives unexpected results.
  
-Scalar values in the array are compared using both type and value, thus similar to the ''==='' operator. Objects of the same class will be compared similarly to the `==` operator, while objects of classes are always seen as not equal.+In the following example ''%%$a%%'' is both greater than and less than ''%%$b%%''
  
 <code php> <code php>
-['a' => 'foo', 'b' => 'bar'] == ['b' => 'bar', 'a' => 'foo'];  // true +$a = ['x' => 1, 'y' => 22]; 
-['a' => 'foo', 'b' => 'bar'] === ['b' => 'bar', 'a' => 'foo']; // false+$b = ['y' => 10, 'x' => 15];
  
-['a' ='foo', 'b' => 'bar'] == ['b' => 'bar', 'a' => 0]     // false (no type juggling)+$a > $b; // true 
 +$< $b; // true
 </code> </code>
  
-=== Object comparison === +==== Strict vs non-strict comparison of arrays ====
-Comparing two objects of different classes using the ''=='' or ''!='' operator will throw a ''TypeError''.+
  
-<code php> +Strict comparison requires that arrays have keys occurring in the same orderwhile non-strict comparison allows out-of-order keys.
-class Foo { +
-  public $x; +
-   +
-  public function __construct($x) { +
-    $this->x = $x; +
-  } +
-+
- +
-class FooBar extends Foo {} +
- +
-(new Foo(10)) == (new Foo(10));     // true +
-(new Foo(10)) == (new Foo(99));     // false +
-(new Foo(10)) === (new Foo(10));    // false +
- +
-(new Foo(10)) == (new FooBar(11));  // TypeError("Type mismatch Foo object and FooBar object for comparison") +
-(new Foo(10)) === (new FooBar(11)); // false +
-</code> +
- +
-Comparing two objects of the same class will with these operators check the properties of the objects. By default, properties are compared in a similar fashion to the ''==='' operator. If the property of both objects contains arrays or objects of the same classthey're compared as using the ''=='' operator. +
- +
-==== Arithmetic operators ==== +
-Arithmetic operators will only work with integers and floats. Using operands of any other type will result in a ''TypeError''+
- +
-In strict type-checking mode, the behavior of the operator is not determined by the value of the operands. Thus for any string, including numeric strings, a ''TypeError'' is thrown, so strings need to be explicitly cast. +
- +
-The ''+'' operator is still available for arrays as union operator, requiring both values to be arrays. +
- +
-==== Incrementing/Decrementing operators ==== +
-The incrementing/decrementing operators will throw a ''TypeError'' when the operand is a boolean, null, array, object or resource. The decrementing operator will also throw a ''TypeError'' if the operand is a string. +
- +
-The function of these operators for integers and floats remains unchanged. +
- +
-=== Incrementing strings === +
-The ''++'' operator for strings will largely remain unchanged, but it will behave consistently for any string, including numeric string. This means that using ''++'' on a string will always result in a string.+
  
 <code php> <code php>
-$a = "00"; +['a=> 'foo', 'b' => 'bar'] == ['b' => 'bar', 'a' => 0]; // true
-++$a; // "01" +
-++$a; // "02" +
-++$a; // "03"+
 </code> </code>
  
-==== Bitwise Operators ==== +To compare the values of two arrays in a strict way while not concerned about the order, requires ordering the array by key prior to comparison.
-Bitwise operators expect both parameters to be an integer. The ''&'', ''|'', ''^'' and ''~'' operators also accept strings as operands.+
  
-Using strings for ''%%>>%%'' or ''%%<<%%'', mixing strings with integers or using any other type will throw a ''TypeError''.+==== Inconsistent behavior ====
  
-==== String Operators ==== +Operators can do any of the following for unsupported operands
-The concatenation operator ''.'' will throw a ''TypeError'' if any of the operands is a boolean, array, resource or null. It will also throw a ''TypeError'' if the operand is an object that doesn't implement the ''%%__toString()%%'' method.+
  
-Integers, floats and objects (with the ''%%__toString()%%'' methodare cast to a string.+  * Cast (silent) 
 +  * Cast with notice / warning 
 +  * Cast with catchable error (fatal) 
 +  * Operator specific notice / warning 
 +  * Operator specific error (fatal) 
 +  * No operation
  
-==== Logical Operators ==== +Please take a look at this [[https://gist.github.com/jasny/bfd711844a8876f8206ed21357e2e2da|list of all combinations of operators and operands]].
-The function of logical operators remains unchanged. All operands are cast to booleans.+
  
-==== Switch control structure ==== +===== Backward Incompatible Changes =====
-When strict-type checking for operators is enabled, the ''switch'' statement will do a comparison similar to a comparison on arrays; Scalar values in the array are compared using both type and value, thus similar to the ''==='' operator. For arrays, the key order does not matter. Objects of the same class will be compared similarly to the `==` operator, while objects of different classes are always seen as not equal. It will **never** throw a ''TypeError''.+
  
-<code php> +None known. As this RFC proposes a new directiveit should only affect code is new or updated to use the strict_operators directive.
-function match($value) +
-+
-  switch ($value) { +
-    case ["foo" => 42, "bar" => 1]: +
-      return "foobar"; +
-      break; +
-    case null: +
-      return "null"; +
-      break; +
-    case 0: +
-      return "zero"; +
-      break; +
-    case "1e1": +
-      return "1e1"; +
-      break; +
-    case "10": +
-      return "10"; +
-      break; +
-    default: +
-      throw new Exception("Unexpected value"); +
-  } +
-+
- +
-match(["bar" => 1"foo" => 42]); // "foobar" +
-match(0);                         // "zero" +
-match("10");                      // "10" +
-match("foo");                     // Exception("Unexpected value"+
-</code+
- +
- +
-===== Backward Incompatible Changes ===== +
-Since the strict type-checking for operators is off by default and must be explicitly used, it does not break backward-compatibility.+
  
 ===== Proposed PHP Version ===== ===== Proposed PHP Version =====
-This is proposed for the next minor version of PHP, currently PHP 7.4. 
  
-===== FAQ =====+This is proposed for PHP 8.0.
  
-==== Why does == and !throw a TypeError instead of returning false? ===+===== Unaffected PHP Functionality =====
-In other dynamically typed languages, like Python and Ruby, the ''=='' and ''!='' do a type check and always return ''false'' in case the type is different. Throwing a ''TypeError'' is more common for statically typed languages like Go.+
  
-PHP already has the ''==='' and ''!=='' operators to check both type and value. This allows ''!='' and ''=='' to follow the same logic as the other comparison operators.+This RFC
  
-==== Why does the concatenation operator cast, but arithmetic operators don't? ==== +  * Does not affect any functionality concerning explicit typecasting. 
-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+  * Is largely unaffected by other proposals like [[rfc/string_to_number_comparison|PHP RFC: Saner string to number comparisons]] that focus on improving type juggling at the cost of breaking BC.
  
-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.+===== FAQ =====
  
-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.+This RFC has an [[rfc/strict_operators/faq|FAQ]] that answers some questions
  
-Using a boolean or null as operand for both concatenation and arithmetic operators also throws a ''TypeError''. In most cases, the use of 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'' [[https://php.net/strpos|''strpos'']] is a well known example. +  * [[rfc/strict_operators/faq#what_has_been_changed_since_the_initial_proposal|What has been changed since the initial proposal?]] 
- +  * [[rfc/strict_operators/faq#why_use_a_directive_instead_of_applying_this_behavior_as_default|Why use a directive instead of applying this behavior as default?]] 
-==== Will comparing a number to a numeric string work with strict operators==== +  [[rfc/strict_operators/faq#why_does_and_only_support_int_and_float_operands|Why does == and != only support int and float operands?]] 
-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 [[https://www.php.net/filter|filter functions]]. +  * [[rfc/strict_operators/faq#why_don_t_comparison_operators_support_strings|Why don't comparison operators support strings?]] 
- +  * [[rfc/strict_operators/faq#why_does_the_concatenation_operator_cast_but_arithmetic_operators_don_t|Why does the concatenation operator cast, but arithmetic operators don't?]] 
-==== Are built-in functions affected by strict_operators? ==== +  * [[rfc/strict_operators/faq#will_comparing_a_number_to_a_numeric_string_work_with_strict_operators|Will comparing a number to a numeric string work with 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. +  [[rfc/strict_operators/faq#how_can_arrays_be_compared_as_unsorted_hashmaps|How can arrays be compared as unsorted hashmaps?]] 
- +  * [[rfc/strict_operators/faq#how_can_objects_be_compared_by_property|How can objects be compared by property?]] 
-Specifically ''sort()'' and ''in_array()'' will perform weak comparison as usual and require the use of the ''$sort_flags'' and ''$strict'' arguments to change the way the values are compared. +  * [[rfc/strict_operators/faq#why_isn_t_is_allowed_to_increment_strings_with_strict_operators|Why isn't is allowed to increment strings with strict_operators?]] 
- +  * [[rfc/strict_operators/faq#are_built-in_functions_affected_by_strict_operators|Are built-in functions affected by strict_operators?]] 
-==== Can relational operators be allowed for arrays? ==== +  * [[rfc/strict_operators/faq#can_relational_operators_be_allowed_for_arrays|Can relational operators be allowed for arrays?]] 
-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. +  * [[rfc/strict_operators/faq#why_is_switch_not_affected|Why is switch not affected?]] 
- +  * [[rfc/strict_operators/faq#are_there_cases_where_a_statement_doesn_t_throw_a_typeerror_but_yields_a_different_result|Are there cases where a statement doesn't throw a TypeError but yields a different result?]] 
-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. +  * [[rfc/strict_operators/faq#will_this_directive_disable_type_juggling_altogether|Will this directive disable type juggling altogether?]]
- +
-==== Are there cases where a statement doesn't throw a TypeError but yields a different result? ==== +
-Yes, there are 3 cases where a comparison works differently in strict operators mode. +
- +
-    Two numeric strings will be compared as strings instead of numbers. Comparing two operands of the same type should always work. Under strict operators, the operation is only determined by the type and never by the value. +
-    * When comparing two arrays (or objects) no typecasting will be performed. So ''[null] == [0]'' will result in ''false'' with strict operators and in ''true'' without the directive. +
-    * With strict operators, the ''case'' in a ''switch'' statement will not do type conversion. It also doesn't compare numeric strings as numbers but as strings. +
- +
-This may cause issues when copying PHP code from one place to another. Also, programmers may intentionally rely on the current semantics when comparing numeric strings. However, as shown in the examples, code that exploits this behavior will likely have unintended side effects. Avoiding this behavior is not a side effect, but the purpose for using the ''strict_operators'' directive. As such, source code with this directive should not depend on this behavior. +
- +
-==== Is it possible to limit the effect of the directive to only throwing a TypeError? ==== +
-No, not in a reasonable way. Many of the motivating examples marked as //unexpected behavior// are caused by modifying the effect of a comparison based on the values of the operands. Keeping that unchanged would defeat the purpose of the RFC. +
- +
-An alternative would be to disallow strings, arrays, and objects as operands for comparison operators, requiring comparison to use functions like ''strcmp'', or introduce new operators. Neither is considered reasonable in respect to this RFC. +
- +
-==== Will this directive disable type juggling altogether? ==== +
-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. +
- +
-Typecasting 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. +
- +
-This RFC limits the scope to operators. +
- +
-==== Why is switch affected? It's not an operator. ==== +
-Internally the ''case'' of a switch is handled as a comparison operator. The issues with ''case'' are therefore similar (or even the same) to those of comparison operators. The audience that ''strict_operators'' caters to, likely want to get rid of this behavior completely. +
- +
-However, since ''case'' isn't an operator, the inclusion will be decided through a secondary vote. +
- +
- +
-===== Unaffected PHP Functionality ===== +
-This RFC +
- +
-  * Does not affect any functionality concerning explicit typecasting. +
-  * Does not affect variable casting that occurs in (double-quoted) strings. +
-  * Is largely unaffected by other proposals like [[rfc:string_to_number_comparison|PHP RFC: Saner string to number comparisons]] that focus on improving type juggling at the cost of breaking BC.+
  
 ===== Implementation ===== ===== Implementation =====
  
-[[https://github.com/php/php-src/pull/4375]]+https://github.com/php/php-src/pull/4375
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
-**Voting ended prematurely and will be reopened at a later stage targeting PHP 8.** 
- 
-    * Primary vote; Add the ''strict_operators'' directive to PHP 7.4? Requires a 2/3 majority 
-    * Secondary vote: Should ''strict_operators'' affect comparison done for a ''case'' operation in a ''switch'' statement? Requires a 50%+1 majority 
  
-<doodle title="Add strict_operators directive?" auth="jasny" voteType="single" closed="true"> +Primary vote: Accept the RFC and merge the patch? Yes/No. Requires a 2/3 majority.
-   Yes +
-   No +
-</doodle>+
  
-<doodle title="Should strict_operator affect switch/case?" auth="jasny" voteType="single" closed="true"> 
-   * Yes 
-   * No 
-</doodle> 
rfc/strict_operators.txt · Last modified: 2020/07/15 14:27 by jasny