Hello internals,
This is the first RFC out of a set of type system related RFCs I want to propose for PHP 8.5.
It also used the recently enabled Markdown support on the wiki, so there might be a few oddities.
The RFC proposes to deprecate implicit type coercions to and from the bool type for other scalar types.
This a "weak" mode change only, as when strict_types are enabled none of these coercions can happen.
Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
Best regards,
Gina P. Banyard
Hello internals,
This is the first RFC out of a set of type system related RFCs I want to propose for PHP 8.5.
It also used the recently enabled Markdown support on the wiki, so there might be a few oddities.The RFC proposes to deprecate implicit type coercions to and from the bool type for other scalar types.
This a "weak" mode change only, as when strict_types are enabled none of these coercions can happen.Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
Best regards,
Gina P. Banyard
No offense, but this feels like it was written by someone who doesn’t use non-strict mode very often. The rules around coercion are pretty simple and well documented. Further, this makes a ton of shorthand nearly impossible — the manual casting to bool in strict mode is one of the most annoying casts someone has to do (and people screw up order of operations all the time leading to subtle bugs worse than coercion). Bringing this into all of php is probably not a good idea.
Type juggling of strings to bool is similarly error-prone. The strings "" and "0" are converted to false, but "false", " ", and other strings which an (int) cast converts to 0 are coerced to true.
If I understand correctly, you are saying it is weird why the bytes 66 61 6C 73 65 are “true” and 30 is a special case, but not 20? If I remember my history correctly, 30 is special-cased because forms used to send “0” on checkboxes (if it sent it at all; it changed from decade to decade).
I don’t see the benefit of removing this implicit coercion.
— Rob
No offense, but this feels like it was written by someone who doesn’t use non-strict mode very often.
As yes, saying this to me, someone that is known to have said on stage, multiple times, during talks that
The strict_types declare was a mistake.
Something I even reiterated 7 weeks ago at a Drupal conference. [1]
It would be nice that you don't make assumption about how I write PHP code, as I use weak and strict mode extensively while being annoyed with both modes constantly.
Moreover, even if one uses strict mode one must still know how weak mode behaves as closures/callables do not follow the typing mode in which they have been declared.
The rules around coercion are pretty simple and well documented.
Considering I have given multiple talks about PHP's type system and type coercions rules, many people do not find them simple or intuitive and get bitten by them constantly.
Further, this makes a ton of shorthand nearly impossible — the manual casting to bool in strict mode is one of the most annoying casts someone has to do (and people screw up order of operations all the time leading to subtle bugs worse than coercion). Bringing this into all of php is probably not a good idea.
It would have been nice to showcase some of these shorthands rather than hiding them behind some sort of mystic as a counterargument.
I really would like to know in which cases you use non-boolean values as argument to boolean parameters.
People creating more subtle and worse bugs due to arbitrary casting is precisely why I want to unify the typing modes, so it's good to know that we think somewhat the same.
Type juggling of strings to bool is similarly error-prone. The strings "" and "0" are converted to false, but "false", " ", and other strings which an (int) cast converts to 0 are coerced to true.
If I understand correctly, you are saying it is weird why the bytes 66 61 6C 73 65 are “true” and 30 is a special case, but not 20? If I remember my history correctly, 30 is special-cased because forms used to send “0” on checkboxes (if it sent it at all; it changed from decade to decade).
I know the reason why we coerce the string "0" to false.
But it is very atypical compared to every other programming language, so yes it is "weird".
I don’t see the benefit of removing this implicit coercion.
The statement is noted.
Regards,
Gina P. Banyard
No offense, but this feels like it was written by someone who doesn’t use non-strict mode very often.
As yes, saying this to me, someone that is known to have said on stage, multiple times, during talks that
The strict_types declare was a mistake.
Something I even reiterated 7 weeks ago at a Drupal conference. [1]
It would be nice that you don't make assumption about how I write PHP code, as I use weak and strict mode extensively while being annoyed with both modes constantly.
Moreover, even if one uses strict mode one must still know how weak mode behaves as closures/callables do not follow the typing mode in which they have been declared.
Hey Gina,
As mentioned, my intent wasn’t to offend. My goal was to tell you how it read, from my perspective, whether you intended it to or not. As in someone trying to force everyone using weak mode to use strict mode. I’m really sorry that it came across that way, though.
The rules around coercion are pretty simple and well documented.
Considering I have given multiple talks about PHP's type system and type coercions rules, many people do not find them simple or intuitive and get bitten by them constantly.
That is a fair point. I’ve seen the same confusion among juniors (and sometimes seniors!) over the years. My sense, though, is that some confusion is less about PHP’s type system itself and more about the unique way PHP handles requests compared to other languages. In most languages, frameworks insulate you from the raw request data and make explicit choices about parsing and typing. In PHP, by contrast, you often get the raw bytes, and it is up to you (or your framework, hopefully) to massage that into proper types—which can lead to surprising behaviours if you’re not careful.
I think sometimes people bring expectations from other ecosystems (where, say, the string “false” might have special meaning) and are surprised when PHP treats “false” like any other non-empty string. Maybe that mismatch is the root of a lot of frustrations?
Curious if you see it the same way, or if there is something more fundamental in PHP’s type rules you think causes the confusion?
Further, this makes a ton of shorthand nearly impossible — the manual casting to bool in strict mode is one of the most annoying casts someone has to do (and people screw up order of operations all the time leading to subtle bugs worse than coercion). Bringing this into all of php is probably not a good idea.
It would have been nice to showcase some of these shorthands rather than hiding them behind some sort of mystic as a counterargument.
I really would like to know in which cases you use non-boolean values as argument to boolean parameters.
People creating more subtle and worse bugs due to arbitrary casting is precisely why I want to unify the typing modes, so it's good to know that we think somewhat the same.
// From a real codebase (sanitized):
$shouldStop = (bool)($result = $service->doSomething($input)) && $result->isError();
This pattern comes up a lot in strict mode: you have to be extra careful with casts and parentheses, or you can end up with subtle bugs like accidentally always returning true/false, or masking null/error values. In looser modes, this just “worked”, but strictness can make it surprisingly easy to trip up.
In my experience, a lot of “non-boolean to boolean” cases are legacy patterns or integrations, but sometimes we’re dealing with values that can be “falsy” in multiple ways (empty array, 0, '', etc.). Sometimes we’re adapting legacy code or writing wrappers that need to accept a wide range of inputs.
The problem, in my view, is that forcing strictness everywhere makes the simple, idiomatic code that worked in PHP for years much more verbose; and potentially more error-prone if the developer isn’t careful with casts or operator precedence.
Type juggling of strings to bool is similarly error-prone. The strings "" and "0" are converted to false, but "false", " ", and other strings which an (int) cast converts to 0 are coerced to true.
If I understand correctly, you are saying it is weird why the bytes 66 61 6C 73 65 are “true” and 30 is a special case, but not 20? If I remember my history correctly, 30 is special-cased because forms used to send “0” on checkboxes (if it sent it at all; it changed from decade to decade).
I know the reason why we coerce the string "0" to false.
But it is very atypical compared to every other programming language, so yes it is "weird".
I will 100% agree with you about "0" being false. It was a handy shortcut 20 years ago, but these days it creates more confusion than it solves.
I don’t see the benefit of removing this implicit coercion.
The statement is noted.
Regards,
Gina P. Banyard
— Rob
Further, this makes a ton of shorthand nearly impossible — the manual casting to bool in strict mode is one of the most annoying casts someone has to do (and people screw up order of operations all the time leading to subtle bugs worse than coercion). Bringing this into all of php is probably not a good idea.
It would have been nice to showcase some of these shorthands rather than hiding them behind some sort of mystic as a counterargument.
I really would like to know in which cases you use non-boolean values as argument to boolean parameters.
People creating more subtle and worse bugs due to arbitrary casting is precisely why I want to unify the typing modes, so it's good to know that we think somewhat the same.// From a real codebase (sanitized):
$shouldStop = (bool)($result = $service->doSomething($input)) && $result->isError();This pattern comes up a lot in strict mode: you have to be extra careful with casts and parentheses, or you can end up with subtle bugs like accidentally always returning true/false, or masking null/error values. In looser modes, this just “worked”, but strictness can make it surprisingly easy to trip up.
I don't get it, you don't need a bool cast here: https://3v4l.org/J4BWi
Even with strict_types: https://3v4l.org/dhnpc
In my experience, a lot of “non-boolean to boolean” cases are legacy patterns or integrations, but sometimes we’re dealing with values that can be “falsy” in multiple ways (empty array, 0, '', etc.). Sometimes we’re adapting legacy code or writing wrappers that need to accept a wide range of inputs.
The problem, in my view, is that forcing strictness everywhere makes the simple, idiomatic code that worked in PHP for years much more verbose; and potentially more error-prone if the developer isn’t careful with casts or operator precedence.
This proposal has no intention of changing the type juggling behaviour of the logical binary operators (and unary ! op) so you shouldn't ever need to use both an operator and a cast (even now with strict_types on).
Best regards,
Gina P. Banyard
The rules around coercion are pretty
simple and well documented.
Just to note, however simple or not simple they are in 8.4, they are substantially simpler and more logical in 8.4 than they were in 7.0 when strict typing was introduced. And most of that improved simplicity is due to Gina's various RFCs along these lines, which have been highly beneficial even if they sometimes get pushback from the "PHP doesn't care about BC" crowd.
That doesn't inherently mean this RFC is good or bad, but the trend/track record has been squeezing the weirdness out of type juggling until weak mode and strict mode are close enough to touch, and the language is better for it.
--Larry Garfield
The RFC proposes to deprecate implicit type coercions to and from the bool type for other scalar types.
This a "weak" mode change only, as when strict_types are enabled none of these coercions can happen.Let me know what you think about it.
Let's consider a legacy (PHP 5) function:
function foo($bool)
{
if ($bool) {
// something
} else {
// something else
}
}
Now the developer wants to add the missing parameter type declaration.
As is, even without static analysis or a test suite, they can just add
bool
, being reasonable sure that the code behaves like before for
scalar types, and only fails for non-scalars, something they may be fine
with. With the suggested change, they can no longer, but would rather
have to inspect and possibly "fix" all callers. Since the latter may
not be possible (perhaps because it is an API function), they would just
stick without type declaration, or use the scalar union type. In my
opinion, either is worse than the current type coercion behavior.
Now, one may argue that there should be no boolean parameters at all,
but besides that this is opiniated, maybe it's the reason why the
developer wants to add the type declaration: to be quickly able to
determine boolean parameters, and to move away from those over time.
So, besides that the proposed change would make it harder to bring
legacy code up to date, it likely breaks a ton of code which already
uses bool
type declarations. That BC break alone is a show-stopper
for me.
Generally, I think that any attempt to unify strict and coercive typing
is futile – that ship has sailed long ago. Those preferring strict
typing likely would not want to compromise, and those preferring
coercive typing will be annoyed by being forced into stricter behavior.
The RFC proposes to deprecate implicit type coercions to and from the bool type
for other scalar types. This a "weak" mode change only, as when strict_types are
enabled none of these coercions can happen.Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
Let's consider a legacy (PHP 5) function:
function foo($bool)
{
if ($bool) {
// something
} else {
// something else
}
}Now the developer wants to add the missing parameter type declaration.
As is, even without static analysis or a test suite, they can just add
bool
, being reasonable sure that the code behaves like before for
scalar types, and only fails for non-scalars, something they may be fine
with. With the suggested change, they can no longer, but would rather
have to inspect and possibly "fix" all callers.
Isn't this already the case? If e.g. someone is passing a nullable string
to the function, adding a bool
type would cause this to break, which
necessitates inspecting all the callers of the function to make sure null
isn't being passed anywhere.
The proposed RFC doesn't change anything for null, but only deprecates
passing float, string, or int to a bool
parameter. In my experience,
such usages almost always indicate bugs, and receiving a deprecation
notice would be valuable for fixing them.
In regards to a unified typing mode, personally I don't see any benefit
from declare(strict_types=1)
anymore, since with static analysis tools
like Psalm strict typing is enforced before even running the code.
Regards,
Theodore
Hello internals,
This is the first RFC out of a set of type system related RFCs I want to propose for PHP 8.5.
It also used the recently enabled Markdown support on the wiki, so there might be a few oddities.The RFC proposes to deprecate implicit type coercions to and from the bool type for other scalar types.
This a "weak" mode change only, as when strict_types are enabled none of these coercions can happen.Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
Best regards,
Gina P. Banyard
Literally 15 minutes ago I caught a nasty bug caused by subj (true
becoming 1 and meaning something else entirely). Well, I love the
proposition.
Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
I would feel better if it targeted PHP9. Right now I'm on +0 on this
proposal.
--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com
Let me know what you think about it.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
I would feel better if it targeted PHP9. Right now I'm on +0 on this
proposal.
I don't understand what you mean.
The purpose is to deprecate the behaviour in 8.5 so that it can be removed in PHP 9.
We try to not break behaviour with no prior warning.
Could you clarify what you mean?
Best regards,
Gina P. Banyard
I don't understand what you mean.
The purpose is to deprecate the behaviour in 8.5 so that it can be removed in PHP 9.
We try to not break behaviour with no prior warning.
Could you clarify what you mean?
I meant to move the deprecation to PHP 9.0. I feel the impact of
deprecation itself in this case might be significant.
--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com
I don't understand what you mean.
The purpose is to deprecate the behaviour in 8.5 so that it can be removed in PHP 9.
We try to not break behaviour with no prior warning.
Could you clarify what you mean?I meant to move the deprecation to PHP 9.0. I feel the impact of
deprecation itself in this case might be significant.
You feel there might be a lot of code passing floats, ints, or strings to
bool
parameters? I'm doubtful this is the case, since static analysis tools
and IDEs are in much more widespread use nowadays.
In my experience passing non-boolean values to a bool
parameter almost always
indicates a bug, and it would be valuable to get a deprecation notice sooner
rather than later so these mistakes can be fixed.
Regards,
Theodore
I don't understand what you mean.
The purpose is to deprecate the behaviour in 8.5 so that it can be removed in PHP 9.
We try to not break behaviour with no prior warning.
Could you clarify what you mean?I meant to move the deprecation to PHP 9.0. I feel the impact of
deprecation itself in this case might be significant.You feel there might be a lot of code passing floats, ints, or strings to
bool
parameters? I'm doubtful this is the case, since static analysis tools
and IDEs are in much more widespread use nowadays.In my experience passing non-boolean values to a
bool
parameter almost always
indicates a bug, and it would be valuable to get a deprecation notice sooner
rather than later so these mistakes can be fixed.Regards,
Theodore
The one place I can see it being used now legitimately is MySQL's TINYINT pseudo-bool columns. If you're mapping your query results into an object (please always do this), then you probably want to cast that into to a bool, and currently weak mode would let you do that implicitly.
I can't think of a case where a string or float magically casting to a bool is reasonable.
--Larry Garfield
I don't understand what you mean.
The purpose is to deprecate the behaviour in 8.5 so that it can be removed in PHP 9.
We try to not break behaviour with no prior warning.
Could you clarify what you mean?
I meant to move the deprecation to PHP 9.0. I feel the impact of
deprecation itself in this case might be significant.
You feel there might be a lot of code passing floats, ints, or strings to
bool
parameters? I'm doubtful this is the case, since static analysis tools
and IDEs are in much more widespread use nowadays.In my experience passing non-boolean values to a
bool
parameter almost always
indicates a bug, and it would be valuable to get a deprecation notice sooner
rather than later so these mistakes can be fixed.Regards,
Theodore
The one place I can see it being used now legitimately is MySQL's TINYINT pseudo-bool columns. If you're mapping your query results into an object (please always do this), then you probably want to cast that into to a bool, and currently weak mode would let you do that implicitly.I can't think of a case where a string or float magically casting to a bool is reasonable.
--Larry Garfield
Basically every time you use external inputs. Maybe it's a $_GET
parameter? I mean, I absolutely do not care whether
$_GET["showcomments"] is actually 0, foo or 1.23. My code will only
assign showcomments=0 or showcomments=1. But all I want is a bool. I
absolutely do not care about the actual contents. If the language warns
me about that it's likely that I will just be annoyed when it happens
occasionally in production when some user tries to play around with the
parameter values. And then I'll explicitly add the cast. But for all
purposes, it's a plain annoyance.
If you desire to be annoyed, be my guest and use strict types.
For me the whole point of weak types is so that I can easily and
comfortably work with arbitrary inputs. So this RFC gets really in my
way here and I'm going to vote no on this.
I could get on board with making true|false decay to bool, whenever they
appear in an union type. But that's - for me - not related to bool in
type juggling.
Bob