-
-
Notifications
You must be signed in to change notification settings - Fork 205
Description
Related to #1589
Analysis
The RFC proposes these 2 changes:
- Emit warning on non-numeric types. Note: since the inspiration for this behavior change comes from
array_reduce(), which throws aTypeErrorfor invalid input types, it's reasonable to assume that in the future, these warnings might get promoted to errors. - For objects that implement the
do_operationorcast_objecthandler internally (not userland), use that handler to cast the value.
Summary of existing behavior in PHP 8.2:
array_sum([null]); // 0
array_sum([false]); // 0
array_sum([true]); // 1
array_sum(['']); // 0
array_sum(['42.5']); // 42.5
array_sum(['42,5']); // 42
array_sum(['42a']); // 42
array_sum(['a42']); // 0
array_sum([[42]]); // 0 (regardless of inner array content)
array_sum([new stdClass()]); // 0
array_sum([gmp_init(42)]); // 0Updated behavior in PHP 8.3:
array_sum([null]); // 0
array_sum([false]); // 0
array_sum([true]); // 1
// Warning: Addition is not supported on type string
array_sum(['']); // 0
array_sum(['42.5']); // 42.5
// Warning: A non-numeric value encountered
array_sum(['42,5']); // 42
// Warning: A non-numeric value encountered
array_sum(['42a']); // 42
// Warning: Addition is not supported on type string
array_sum(['a42']); // 0
// Warning: Addition is not supported on type array
array_sum([[42]]); // 0 (regardless of inner array content)
// Warning: Addition is not supported on type stdClass
array_sum([new stdClass()]); // 0
array_sum([gmp_init(42)]); // 42 (whereas PHP 8.2 treated this like any other object and resulted in 0)Top 2000 Packages
Found 270 occurrences of array_sum calls, and 5 occurrences of array_product calls. All usages rely on a variable input.
GMP and FFI functions seem to be used even more frequently, so we should definitely consider them when checking castability.
Detection in PHP 8.2
Since behavior is the same for array_sum() and array_product(), I'll focus on array_sum():
array_sum([''])validarray_sum(['42a'])validarray_sum(['a42'])validarray_sum([[]])validarray_sum([new stdClass()])validarray_sum([gmp_init(42)])validarray_sum([(int)'a42'])valid
See "Syntax Variations" for false positives and behavior that remains unchanged.
Detection in PHP 8.3
Since behavior is the same for array_sum() and array_product(), I'll focus on array_sum():
array_sum([''])warningarray_sum(['42a'])warningarray_sum(['a42'])warningarray_sum([[]])warningarray_sum([new stdClass()])warningarray_sum([gmp_init(42)])result changes because GMP can cast to int internally (I recommend treating this as a warning in PHPCompatibility)array_sum([(int)'a42'])valid, no behavior change
See "Syntax Variations" for false positives and behavior that remains unchanged.
Syntax Variations
- Casting examples with array_sum - invalid/undesirable casts emit warnings since PHP 8.3
- Casting examples with array_product - invalid/undesirable casts emit warnings since PHP 8.3
- Explicitly cast before using in array_sum - only stdClass emits a warning, and same behavior across both versions
Detectability
- Find calls to
array_sum()orarray_product()✅ Trivial in PHPCompatibility - Find values passed to these functions ✅❌ Limited ability, as these can come from anywhere
- Find types of array elements ✅❌ Depends on finding the values, detecting the type itself should be straightforward in many cases
- Detect usage of
gmp_*functions in array elements ✅❌ Depends on finding the values, detecting thegmp_*call itself should be straightforward in many cases (as far as I can tell, every such function returns aGMPtype, which is castable) - Detect usage of
FFIfunctions in array elements ✅❌ Depends on finding the values
It's unclear which other built-in classes implement the do_operation or cast_object handlers, and my C proficiency is insufficient to determine that myself. Should we enumerate them for this sniff?
This seems to be a job for a static analysis tool that can trace input and infer types. Opened tickets for both PHPStan and Psalm regardless of whether we'll create a sniff here.
References
- RFC: Saner array_(sum|product)()
- PR: Saner array_(sum|product)()
- PHP Manual:
- PHP Internals Book:
- cast_object - applies to internal classes, not userland (helps understand the GMP example)
- do_operation - applies to internal classes, not userland (helps understand the GMP example)
Edit: added explicit casting of values in examples and detection