rfc:saner-array-sum-product

Differences

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

Link to this comparison view

Next revision
Previous revision
rfc:saner-array-sum-product [2023/01/14 21:06] – Created girgiasrfc:saner-array-sum-product [2023/03/07 15:43] (current) girgias
Line 1: Line 1:
 ====== PHP RFC: Saner array_(sum|product)()  ====== ====== PHP RFC: Saner array_(sum|product)()  ======
  
-  * Version: 0.1+  * Version: 0.2
   * Date: 2023-01-14   * Date: 2023-01-14
   * Author: George Peter Banyard, <girgias@php.net>   * Author: George Peter Banyard, <girgias@php.net>
-  * Status: Under Discussion+  * Status: [[https://github.com/php/php-src/commit/3b06618813fe0979850eaa1f4bed426edb5b3123|Implemented]]
   * Target Version: PHP 8.3   * Target Version: PHP 8.3
   * Implementation: [[https://github.com/php/php-src/pull/10161]]   * Implementation: [[https://github.com/php/php-src/pull/10161]]
Line 25: Line 25:
 If the entries in the input array are exclusively comprised of int and float values, then the behaviour between the built-in functions and their userland implementation is identical. However, if other types are part of the entries, the behaviour diverges. If the entries in the input array are exclusively comprised of int and float values, then the behaviour between the built-in functions and their userland implementation is identical. However, if other types are part of the entries, the behaviour diverges.
  
-We believe that the behaviour between the two variants should be as close as possible, as we will use this as our design principle for addressing the discrepancies between both implementations.+We believe that the behaviour between the two variants should be as close as possible, and we will use this as our design principle for addressing the discrepancies between both implementations.
  
 Therefore, we will first look at the behaviour of the <php>array_reduce()</php> version which the arithmetic operators with various types, then detail the current behaviour of the <php>array_sum()</php> and <php>array_product()</php> functions, and finally propose various changes to fix the discrepancies. Therefore, we will first look at the behaviour of the <php>array_reduce()</php> version which the arithmetic operators with various types, then detail the current behaviour of the <php>array_sum()</php> and <php>array_product()</php> functions, and finally propose various changes to fix the discrepancies.
Line 31: Line 31:
 ==== Behaviour of the array_reduce() variants === ==== Behaviour of the array_reduce() variants ===
  
-As the <php>array_reduce()</php> variants use arithmetic operators. It's crucial to understand how they perform with non numeric values. Arithmetic operators perform a numeric type juggling, which is described in the userland manual as:+As the <php>array_reduce()</php> variants use arithmetic operators. It's crucial to understand how they perform with non-numeric values. Arithmetic operators perform a numeric type juggling, which is described in the userland manual as:
  
 <blockquote> <blockquote>
Line 47: Line 47:
 All other cases throw a ''TypeError''. All other cases throw a ''TypeError''.
  
 +=== Examples ===
 +
 +Example with <php>GMP</php> objects:
 +<PHP>
 +$input = [gmp_init(6), gmp_init(3), gmp_init(5)];
 +
 +// Userland implementation of array_sum($input)
 +$output = array_reduce($input, fn($carry, $value) => $carry + $value, 0);
 +var_dump($output);
 +/*
 +object(GMP)#5 (1) {
 +  ["num"]=>
 +  string(2) "14"
 +}
 +*/
 +
 +// Userland implementation of array_product($input)
 +$output = array_reduce($input, fn($carry, $value) => $carry * $value, 1);
 +var_dump($output);
 +/*
 +object(GMP)#6 (1) {
 +  ["num"]=>
 +  string(2) "90"
 +}
 +*/
 +</PHP>
 +
 +Pathological example:
 +<PHP>
 +/* STDERR gets cast to 3 */
 +$input = [true, STDERR, new stdClass(), [], gmp_init(6)];
 +
 +// Userland implementation of array_sum($input)
 +$output = array_reduce($input, fn($carry, $value) => $carry + $value, 0);
 +var_dump($output); // TypeError
 +
 +// Userland implementation of array_product($input)
 +$output = array_reduce($input, fn($carry, $value) => $carry * $value, 1);
 +var_dump($output); // TypeError
 +</PHP>
 +
 +Certain FFI\CData types implements a ''do_operation'' handler for addition:
 +<PHP>
 +$x = FFI::new("int[2]");
 +$x[0] = 10;
 +$x[1] = 25;
 +
 +$input = [$x, 1];
 +
 +$output = array_reduce($input, fn($carry, $value) => $carry + $value, 0);
 +var_dump($output);
 +/*
 +object(FFI\CData:int32_t*)#4 (1) {
 +  [0]=>
 +  int(25)
 +}
 +*/
 +</PHP>
  
 ==== Current behaviour of the array_sum() and array_product() functions ==== ==== Current behaviour of the array_sum() and array_product() functions ====
Line 58: Line 116:
   * Otherwise, the entry is cast to a number (int|float), and this value is added/multiplied to the return value.   * Otherwise, the entry is cast to a number (int|float), and this value is added/multiplied to the return value.
  
-As such resource, array, non-numeric strings, and non-addable/multiplicate and non-numerically castable objects entries do not throw a ''TypeError'', behaviour which was made consistent in PHP 8.0.0 with the [[rfc:arithmetic_operator_type_checks|Stricter type checks for arithmetic/bitwise operators] RFC.+As such resource, array, non-numeric strings, and non-addable/multiplicate and non-numerically castable objects entries do not throw a ''TypeError'', behaviour which was made consistent in PHP 8.0.0 with the [[rfc:arithmetic_operator_type_checks|Stricter type checks for arithmetic/bitwise operators]] RFC. 
 + 
 +=== Examples === 
 + 
 +Example with <php>GMP</php> objects: 
 +<PHP> 
 +$input = [gmp_init(6), gmp_init(3), gmp_init(5)]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); // int(0) 
 + 
 +$output = array_product($input); 
 +var_dump($output); // int(1) 
 +</PHP> 
 + 
 +Pathological example: 
 +<PHP> 
 +/* STDERR gets cast to 3 */ 
 +$input = [true, STDERR, new stdClass(), [], gmp_init(6)]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); // int(4) 
 + 
 +$output = array_product($input); 
 +var_dump($output); // int(3) 
 +</PHP> 
 + 
 + 
 +FFI example: 
 +<PHP> 
 +$x = FFI::new("int[2]"); 
 +$x[0] = 10; 
 +$x[1] = 25; 
 + 
 +$input = [$x, 1]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); // int(1) 
 +</PHP>
  
 ===== Proposal ===== ===== Proposal =====
  
-The proposal is to use the same behaviour for <php>array_sum()</php> and <php>array_product()</php> as the <php>array_reduce()</php> variants with the caveat that if the return value would be an object it needs to be numerically castable to respect the current return type of ''int|float''.+The proposal is to use the same behaviour for <php>array_sum()</php> and <php>array_product()</php> as the <php>array_reduce()</php> variants by reusing the engine functions that perform the arithmetic operations while still accepting, but emitting an <php>E_WARNING</php>, and implementing the current behaviours for values that are rejected by the arithmetic operators. 
 + 
 +The one caveat is that objects must implement a numeric cast for them to be added/multiplied, this is done to respect the current return type of ''int|float''. 
 + 
 +Therefore, the previous three examples would result in the following behaviour: 
 + 
 +Example with <php>GMP</php> objects: 
 +<PHP> 
 +$input = [gmp_init(6), gmp_init(3), gmp_init(5)]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); // int(14) 
 + 
 +$output = array_product($input); 
 +var_dump($output); // int(90) 
 +</PHP> 
 + 
 +Pathological example: 
 +<PHP> 
 +$input = [true, STDERR, new stdClass(), [], gmp_init(6)]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); 
 +/* 
 + 
 +Warning: array_sum(): Addition is not supported on type resource in %s on line %d 
 + 
 +Warning: array_sum(): Addition is not supported on type stdClass in %s on line %d 
 + 
 +Warning: array_sum(): Addition is not supported on type array in %s on line %d 
 +int(10) 
 +*/ 
 + 
 +$output = array_product($input); 
 +var_dump($output); 
 +/* 
 + 
 +Warning: array_product(): Multiplication is not supported on type resource in %s on line %d 
 + 
 +Warning: array_product(): Multiplication is not supported on type stdClass in %s on line %d 
 + 
 +Warning: array_product(): Multiplication is not supported on type array in %s on line %d 
 +int(18) 
 +*/ 
 + 
 +</PHP> 
 +FFI example: 
 +<PHP> 
 +$x = FFI::new("int[2]"); 
 +$x[0] = 10; 
 +$x[1] = 25; 
 + 
 +$input = [$x, 1]; 
 + 
 +$output = array_sum($input); 
 +var_dump($output); // int(1) 
 +/* 
 + 
 +Warning: array_sum(): Addition is not supported on type FFI\CData in %s on line %d 
 +int(1) 
 +*/ 
 +</PHP>
  
-The new implementation reuses the engine functions that perform the arithmetic operations while still accepting, but emitting an <php>E_WARNING</php>, and implementing the current behaviours for values that are rejected by the arithmetic operators. 
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 70: Line 226:
 <php>E_WARNING</php>s are emitted for incompatible types. <php>E_WARNING</php>s are emitted for incompatible types.
  
-If traversing the array transforms the return value into an object, if that object is not numerically castable an <php>E_WARNING</php> is emitted and the initial return value is returned instead. +Arrays that contain objects may now produce different results, for example:
- +
-Arrays that mix scalar values and objects may now produce different results, for example:+
 <PHP> <PHP>
 $a = [10, 15.6, gmp_init(25)]; $a = [10, 15.6, gmp_init(25)];
Line 79: Line 233:
 Currently, results in: ''float(25.6)'' but with this proposal accepted would result in: Currently, results in: ''float(25.6)'' but with this proposal accepted would result in:
 <PHP> <PHP>
-Deprecated: Implicit conversion from float 25.6 to int loses precision in %s on line %d +int(50.6)
-int(50)+
 </PHP> </PHP>
  
Line 92: Line 245:
 As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted.
  
-Voting started on 2023-XX-XX and will end on 2023-XX-XX.+Voting started on 2023-02-20 and will end on 2023-03-06.
 <doodle title="Accept Saner array_(sum|product)() RFC?" auth="girgias" voteType="single" closed="true"> <doodle title="Accept Saner array_(sum|product)() RFC?" auth="girgias" voteType="single" closed="true">
    * Yes    * Yes
Line 101: Line 254:
  
 GitHub pull request: https://github.com/php/php-src/pull/10161 GitHub pull request: https://github.com/php/php-src/pull/10161
 +
 +Landed in PHP 8.3:
 +Implemented via commit: https://github.com/php/php-src/commit/3b06618813fe0979850eaa1f4bed426edb5b3123
  
 After the project is implemented, this section should contain After the project is implemented, this section should contain
rfc/saner-array-sum-product.1673730399.txt.gz · Last modified: 2023/01/14 21:06 by girgias