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.
Hi Gina
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).
Every cast-to-bool example you are providing are int-to-bool coercions, not string nor float casts type.
Especially that even within SFs test suite, various cases of string-to-bool casts seems to be highlighting some sort of bug in the test suite.
Moreover, the fixes for the bitwise & operator are following the styles from surrounding functions that already do that, and I was slightly confused about the inconsistencies.
The preg_match()
API is just unfortunate that it returns a boolean value as integers rather than booleans, as it uses a false return to communicate it emitted a warning instead of null.
Improving this API, is a very orthogonal problem to this, that we probably should be doing anyway (e.g. see if we can promote the warnings to exceptions).
And I could be convinced to carve out an exception for int-to-bool, however that retains some complexity remembering PHP's type juggling rules, or have it as a secondary vote.
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.
As said above, it is even possible to only warn on string- and float-to-bool and not on int-to-bool coercions.
But I still think theoretical angles are important, because allowing edge cases always adds complexity that has causes us countless headaches in the past.
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.)
I'm not exactly certain I buy the argument that delaying this deprecation is worthwhile, especially as I don't really think is as high impact as it is made out to be.
And I don't really see what "variants" there are to explore other than a possible allowance for int-to-bool.
As we are trading time for users to fix their code upcoming to PHP 9 for open source libraries where many use strict_types and as such are not affected by this change.
Best regards,
Gina P. Banyard
On Monday, 30 June 2025 at 10:26, Nicolas Grekas wrote:
...
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. anstrpos()
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).
From my experience there is a very real benefit to deprecating both cast-from-bool and cast-to-bool.
Let me give an example of both.
Recently had to fix some legacy code where the result of filemtime
was being passed to gmdate
to format
the modification timestamp. filemtime
returns false on failure, but this is silently coerced to zero
(a valid timestamp) when passed to the int parameter, which would result in the script continuing with bad
data instead of halting with a type error.
Cast-to-bool can cause the same kinds of issues, which are sometimes even harder to notice. Consider the following:
function processArray(array $items, bool $hasCertainKey) { ... }
$keyOrFalse = array_search('my value', $items);
processArray($items, $keyOrFalse);
It's very easy to miss the bug in this code, especially since it seems to function correctly as long as 'my value'
is not in the array, or is not the first item in the array. However, when it's at index 0 the key will be silently
coerced to false and the array will be incorrectly processed as though the value is not in the array.
I believe deprecating type juggling to/from bool will help avoid creating bugs like this (and likely
surface existing ones that should be fixed).
Kind regards,
Theodore
On Monday, 30 June 2025 at 10:26, Nicolas Grekas wrote:
...
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. anstrpos()
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).From my experience there is a very real benefit to deprecating both cast-from-bool and cast-to-bool.
Let me give an example of both.Recently had to fix some legacy code where the result of
filemtime
was being passed togmdate
to format
the modification timestamp.filemtime
returns false on failure, but this is silently coerced to zero
(a valid timestamp) when passed to the int parameter, which would result in the script continuing with bad
data instead of halting with a type error.Cast-to-bool can cause the same kinds of issues, which are sometimes even harder to notice. Consider the following:
function processArray(array $items, bool $hasCertainKey) { ... } $keyOrFalse = array_search('my value', $items); processArray($items, $keyOrFalse);
It's very easy to miss the bug in this code, especially since it seems to function correctly as long as 'my value'
is not in the array, or is not the first item in the array. However, when it's at index 0 the key will be silently
coerced to false and the array will be incorrectly processed as though the value is not in the array.I believe deprecating type juggling to/from bool will help avoid creating bugs like this (and likely
surface existing ones that should be fixed).Kind regards,
Theodore
I'm not so confident that will be the case. In lots of strict-mode code I've worked with over the years, people tend to blindly cast things without realizing they're in a worse situation than using non-strict mode. Like ((int)"123password") === 123
, however attempting to coerce that string to an int results in a proper type error in non-strict mode.
That is to say, I highly suspect that gmdate(filemtime($file))
would just be rewritten as gmdate((int)filemtime($file))
without a second thought.
In your other example showing the difference in array_search, I also highly suspect people would just cast it to bool without noticing the 0-key edge case.
Both cases, I believe, can simply be solved by tooling highlighting when you're passing a union type to a function that doesn't overlap with the function's expectations. ("you are passing int|string to a function expecting bool" and/or "you are casting int|string to bool, check that (bool)"0" won't be an edge-case; consider using empty() instead").
— Rob
Le ven. 11 juil. 2025 à 13:00, Gina P. Banyard internals@gpb.moe a écrit :
On Monday, 30 June 2025 at 10:26, Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:Hi Gina
Thanks for experimenting with this on
https://github.com/symfony/symfony/pull/60890/filesIt'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).Every cast-to-bool example you are providing are int-to-bool coercions,
not string nor float casts type.
That's correct. I feel quite strongly about this: int-to-bool casts should
be handled gracefully by the engine seamlessly. Deprecating them will (plan
to) break many apps that are just fine, and as Rob wrote (and you also in a
gist), casting to (bool) is what many will do, wrongly. This won't improve
the language, quite the contrary.
About string-to-bool PHP is still that language that allows quickly
consuming $_GET['foo'] as whatever it looks like as a string. I suspect
tons of codebases rely on this behavior. To me, that's where the costs
might outweigh the benefits...
Especially that even within SFs test suite, various cases of string-to-bool
casts seems to be highlighting some sort of bug in the test suite.
Moreover, the fixes for the bitwise & operator are following the styles
from surrounding functions that already do that, and I was slightly
confused about the inconsistencies.
That's an inconsistency favored by the strict-mode hype: people blindly
cast (and introduce bugs)... That inconsistency should be fixed the other
way around: removing these needless (and dangerous) casts.
The
preg_match()
API is just unfortunate that it returns a boolean value
as integers rather than booleans, as it uses a false return to communicate
it emitted a warning instead of null.
Unfortunate but still to be acknowledged as a likely contribution to making
this RFC a high-cost one for userland.
Improving this API, is a very orthogonal problem to this, that we probably
should be doing anyway (e.g. see if we can promote the warnings to
exceptions).
And I could be convinced to carve out an exception for int-to-bool,
however that retains some complexity remembering PHP's type juggling rules,
or have it as a secondary vote.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.
As said above, it is even possible to only warn on string- and
float-to-bool and not on int-to-bool coercions.
But I still think theoretical angles are important, because allowing edge
cases always adds complexity that has causes us countless headaches in the
past.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.)I'm not exactly certain I buy the argument that delaying this deprecation
is worthwhile, especially as I don't really think is as high impact as it
is made out to be.
Deprecating implicit null-ness was (still is) a high impact deprecation,
and this one has a higher potential impact IMHO.
And I don't really see what "variants" there are to explore other than a
possible allowance for int-to-bool.
The variant I suggested is to relax the true and false types and make them
not-strict when not in strict-mode. Your motivation for the RFC is to make
the bool type equivalent to true|false. This can be achieved by relaxing
true and false types.
Nicolas
PS: what about null-forwarding cast operators? (?bool) to let null get
through the cast? Just throwing in case it reaches anyone :)
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
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 didn't think adding examples for "to" bool was useful, but I can add some.
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.
I forgot to update the optimized strlen opcode, I pushed a fix for this, please pull in the latest version of the branch that has been force pushed.
- 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?
I haven't had time to look at why this is not triggering as it should. But some internal functions do weird stuff sometimes.
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
Best regards,
Gina P. Banyard
Am 01.07.2025 um 14:40 schrieb Gina P. Banyard internals@gpb.moe:
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 didn't think adding examples for "to" bool was useful, but I can add some.
Thanks!
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.
I forgot to update the optimized strlen opcode, I pushed a fix for this, please pull in the latest version of the branch that has been force pushed.
Yes, that fixed it for me.
- 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?
I haven't had time to look at why this is not triggering as it should. But some internal functions do weird stuff sometimes.
Not sure if it was a side-effect of the other fix or if you fixed more but the code above now correctly reports a warning.
Thanks again, my evaluation with our code base show very little impact so I'm positive about the change,
- Chris
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.
I am sorry to say, but this RFC seems to be mostly to have a 'purity'
argument behind it.
The RFC also does not seems to include an analysis
of the impact.
I can almost guarantee you that this is going to 'break'
(by a deprecation warning?) a lot of code. Even the changes made for
symfony seem non-trivial, and easy to get wrong
(https://github.com/symfony/symfony/pull/60890/files).
But we don't know, as there is no data on this — that's why I feel that
this is not a mature enough RFC.
cheers,
Derick
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
As the discussion doesn't feel productive any longer as this is now just about clashing opinions I will open the vote in the coming days.
I have slightly changed the wording and expanded on some points, but the RFC is effectively unchanged.
RFC: https://wiki.php.net/rfc/deprecate-function-bool-type-juggling
Best regards,
Gina P. Banyard