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
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
I have updated the RFC to version 0.2 that expands on it and addresses some of the counterarguments which were said during the discussion.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
If there is no-follow up feedback, I will open the vote for it sometime next week.
Best regards,
Gina P. Banyard
Hi Gina
Le jeu. 26 juin 2025 à 14:07, Gina P. Banyard internals@gpb.moe a écrit :
On Monday, 2 June 2025 at 17:11, Gina P. Banyard internals@gpb.moe
wrote: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
I have updated the RFC to version 0.2 that expands on it and addresses
some of the counterarguments which were said during the discussion.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-jugglingIf there is no-follow up feedback, I will open the vote for it sometime
next week.
Thanks for experimenting with this on
https://github.com/symfony/symfony/pull/60890/files
It's good to see that fitting this RFC requires a relatively small number
of lines to change even on a codebase like Symfony. We're not at 100% but
still nice.
Yet, it IS a big bang in terms of deprecations to fix that this will
trigger. Their number is super high, because these boolean
conversions happen on hotpath / in loops.
This reminds me of the mandatory "?" for arguments with null defaults: the
change made sense from a language design PoV, but the impact was, and still
is very high. It was and still is a very costly deprecation for the
community, and it's not over yet. That made me say at conferences that this
was maybe not a good idea...
Looking at the diff above, I think I can draw two sets of changes:
- the ones that patch a cast-to-bool
- the ones that patch a cast-from-bool
The latter, cast-from-bool, I think they're all useful improvements. While
most likely harmless in all the specific cases of the PR, doing e.g. an
strpos()
on false feels hardly legit. I'm therefore sympathetic to making
these changes.
For cast-to-bool, I'm WAY less convinced. From the PR above, explicit casts
like "return (bool) preg_match(...)" on a method that returns a "bool" or "(
bool) ($this->loggedErrors & $type)" are a clear downgrade: it makes the
typing PHP just more verbose without any real benefit. That doesn't look
worth asking the whole ecosystem to fix those deprecations. It is
especially hard to see this as an improvement when comparing to using the
same expressions with e.g. the "if ()" operator, which doesn't need the
explicit cast (and shouldn't of course).
In the RFC, you write that its motivation is to allow making the bool type
equivalent to the true|false union. To do so you propose to make the bool
type always behave like in "strict" mode.
Personally, I'm moot about the motivation. I understand it, but it looks
inappropriate to push deprecations from this somewhat (IMHO) theoretical
angle. The added friction would need way stronger arguments to be justified
IMHO.
Would it be possible to trigger the deprecation only in the cast-from-bool
direction, and accept casts-to-bool as seamlessly as there are now? If yes,
then would it also be possible to make the true and false types have the
same behavior (aka non-strict in casts-to-bool direction - when not in
strict mode of course)? If yes to both questions, then that'd look like a
better outcome that'd still allow fulfilling your goal, while providing
maximum value to the community.
As is, the cost/benefit ratio doesn't make this RFC look worth it to me.
Last but not least, I'm not sure it's a good idea to rush this into 8.5.
This is a high-impact change. We should give some time to the discussion,
understand the impact and explore variants. Merging this in 8.6 would also
give a few more months for open-source projects to adapt to the change
(since many do run their CI with the master branch of PHP to spot changes
as early as possible.)
Cheers,
Nicolas
PS: I'm going AFK for the next ~7 days.
Am 26.06.2025 um 14:04 schrieb Gina P. Banyard internals@gpb.moe:
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
I have updated the RFC to version 0.2 that expands on it and addresses some of the counterarguments which were said during the discussion.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-jugglingIf there is no-follow up feedback, I will open the vote for it sometime next week.
The RFC talks about "from" AND "to" bool conversion but the examples under Backward Incompatible Changes only seem mention "from" bool, is that correct?
I tried the implementation in branch bool-juggling-dep-poc on https://github.com/Girgias/php-src.git and encountered two things:
- php -r 'strlen(false);' produced a core dump with Zend/zend_API.c:561: zend_arg_from_bool_deprecated: Assertion `func->common.fn_flags & (1 << 14)' failed.
- array_slice($a, 1, preserve_keys:"a") did not produce a warning even though I would have thought that is a "to" bool conversion. Am I wrong? What should I test instead?
So far the "from" conversion warnings need changes in very few places in our setup (we very occasionally use false as out-of-band signalling and use the side-effect that strlen()
can check both for empty string and false at the same time but that can be easily adapted), our lesson from the nullable parameter that composer packages are the more interesting caveat than our our own but so far nothing popped up.
Summary: I am mostly positive about the "from" part, I don't think I was able to test the "to" part and I'm not sure why I triggered an assertion.
Regards,
- Chris