-
-
Notifications
You must be signed in to change notification settings - Fork 817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix input type for smarty number formatting (more forgiving) #22429
Conversation
(Standard links)
|
Civi/Core/Format.php
Outdated
@@ -33,7 +33,10 @@ class Format { | |||
* @noinspection PhpDocMissingThrowsInspection | |||
* @noinspection PhpUnhandledExceptionInspection | |||
*/ | |||
public function money(string $amount, ?string $currency = NULL, ?string $locale = NULL): string { | |||
public function money($amount, ?string $currency = NULL, ?string $locale = NULL): string { | |||
if (is_null($amount) || $amount === '') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about is (strlen($amount) === 0) ?
that would get FALSE too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the value ever be FALSE
? And if it could be a bool, what on earth would TRUE
mean? How much money is TRUE
worth?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@colemanw I doubt it would ever be TRUE but we DO sometimes fill our arrays with FALSE where we want to avoid enotices
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm wary of type coercion, passing numbers into strlen()
sounds like an invitation for weird bugs. I'd rather just add || is_bool($amount)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The monetary value of TRUE is USD $1 - I think it's in the Geneva Convention. Seriously though I'd argue it should throw an error if TRUE is passed in.
Between this and the other PR, I get the tradeoff between being able to catch bugs/typos early (being strict) and between not having to track down every existing caller (being forgiving), and normally I like the former, but my 2c on this one is to prefer this PR.
Regarding what the if
should look like, maybe something like:
// Handle values that Brick Money doesn't like
// We also treat FALSE as '' because sometimes variables are mass-filled with FALSE to avoid being missing, so can't be treated as an error.
if (is_null($amount) || $amount === '' || $amount === FALSE) {
return '';
}
elseif ($amount === TRUE) {
throw new CRM_Core_Exception('TRUE is not a valid value for type money');
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I've updated this PR to forgive FALSE
, NULL
and ''
but throw an exception for all other non-numeric values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with that!
e519266
to
c2e0c7d
Compare
retest this please |
@colemanw that looks like it was a real fail. It seems the TokenProcessor assigns objects of type Money to the token values when it thinks it's a money field, and then it always passes a BigDecimal as the value (via getAmount()). But because it has a
From my local, and if I put the typehint back it passes, confirming the autocasting:
|
Civi/Core/Format.php
Outdated
if (is_null($amount) || $amount === '' || $amount === FALSE) { | ||
return ''; | ||
} | ||
elseif (!is_numeric($amount)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what might work as a general catchall is elseif (!is_numeric((string) $amount)) {
Or explicitly only cast BigDecimals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no nevermind that would make TRUE be '1'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about elseif ($amount === TRUE || !is_numeric((string) $amount)) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
murmurring supportive noises (I don't have much to add since you two seem to be on the right track here)
Some smarty templates pass NULL to crmMoney, so it needs to handle that possibilty
c2e0c7d
to
c1c02f4
Compare
Yay! Passing tests. |
Overview
Fixes a hard-crash with CiviGrant, regression in 5.46 caused by #22309 .
This is a more forgiving version of #22427
Before
After
Fixed.
Technical Details
There were confusing mismatches between docblocks and the function signatures.
smarty_modifier_crmMoney
had$amount
annotated as typefloat
but passed it intoFormat::money
which requires typestring
, which passes it ontoMoney::of
which accepts typesBigNumber|int|float|string
.I think I've straightened it out.