-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Description
Preconditions and environment
Preconditions
- Magento 2.4.8-p3 (Community)
- PHP 8.4.x (tested with PHP 8.4.13 CLI / FPM)
- Credit memo creation via API where the quantity is provided as a string
- Strict types enforced in Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty()
Technical Details
Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty() (and related logic) relies on min() while the function signature requires a strict float type. PHP’s min() is sensitive to both argument order and type juggling:
Be careful when passing arguments of different types because min() can produce unpredictable results.
https://www.php.net/manual/en/function.min.php
With PHP 8.4 and strict typing, this is no longer just “unpredictable” logically, it can result in a hard TypeError when one of the values is a string but the parameter type is declared as float.
We also noticed that installing extensions like Blackfire can change how the quantity is parsed (e.g. the same value is now returned as a float instead of a string), which hides the bug because the types happen to align again.
Proposed Solution
Remove or avoid the direct use of min() when the method signature requires strict float arguments.
Ensure all quantities are explicitly cast to float (or properly validated and converted) before passing them to isValidDecimalRefundQty() and/or before calling min().
Alternatively, replace min() usage with explicit numeric comparison logic that operates on well-typed float values, e.g.:
$qty = (float)$qty;
$itemQty = (float)$itemQty;
$validatedQty = $qty <= $itemQty ? $qty : $itemQty;
This keeps the method signature correct and avoids unexpected type juggling from min() with mixed string/float inputs.
Steps to reproduce
- Install Magento 2.4.8-p3 on PHP 8.4
- Create an order with at least one shippable item
- Create a credit memo via API or custom code where the qty value is passed as a string (e.g. "1.0")
- Trigger the credit memo creation / refund so that Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty() is executed
- Observe the PHP error.
Expected result
- Credit memo quantity validation handles string/float input safely
- No TypeError is thrown
The quantity is correctly validated and the credit memo is created or a proper validation error is returned.
Actual result
A TypeError is thrown:
TypeError: Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty(): Argument #2 ($itemQty) must be of type float, string given, called in vendor/magento/module-sales/Model/Order/Creditmemo/Validation/QuantityValidator.php on line 136 and defined in vendor/magento/module-sales/Model/Order/Creditmemo/Validation/QuantityValidator.php:107
The root cause is that the code uses min() with values that are not guaranteed to be float, even though the method signature for $itemQty is float. When a string quantity is passed in, PHP 8.4’s behavior plus strict typing leads to the TypeError.
Additional information
With certain extensions installed (e.g. Blackfire), the behavior of the quantity parsing changes and the value becomes a float instead of a string, so the error does not occur. This makes the problem harder to spot.
Release note
No response
Triage and priority
- Severity: S0 - Affects critical data or functionality and leaves users without workaround.
- Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
- Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
- Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
- Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status