[PHP-DEV] [RFC] Deprecate Fuzzy Type Casts and Allow Stringable in Strict Mode

On Fri, Jan 23, 2026 at 3:17 PM Alexandre Daubois <alex.daubois+php@gmail.com> wrote:

I understand the concern about legacy code. However, if code relies on
(int) "123abc" silently returning 123, this represents a data
integrity issue that would benefit from being made explicit. The
deprecation period exists specifically to help identify and address
these cases.

Does this behavior als affect parameters being passed in a non-strict fashion? Because then I don’t see a realistic migration path within the next decade for this application.

This behavior already exists and forbids such casts, see
https://3v4l.org/WQJPv#vnull.

Thanks for your reply!

I’ve given it a bit more thought and while I 100% agree with the reasons given, I’m still afraid this change is too big of an unknown break. I do not have voting power, so all I can give is my opinion.

As an alternative, what about this? It could be the best of both worlds where the new behavior is explicit:

$var = (int) '123test'; // still works, nothing changes, becomes an `(int) 123`
int $var = '123test'; // follows the proposed rules, errors because it's not a value that's a valid integer
int $var = '123'; // works because it's a valid integer format, becomes (int) 123
int $var = (int) '123test'; // becomes (int) 123, existing behavior

This way nothing breaks and the casting remains what it is, but we can still enforce correct types. I know the 3rd line might be controversial, but it makes it an opt-in instead of opt-out feature and errors can be thrown as of day 1.

Bonus:

int|null $var = '123test'; // could result in `null` instead of throwing, similar to the Enum tryFrom.

// could enable
MyEnum $foo = '123test';MyEnum|null $bar = '123test';

// translates to
$foo = MyEnum::from('123test');$bar = MyEnum::tryFrom('123test');

from and tryFrom could be taken as base for custom cast functions, but that’s probably a whole different can of worms.

On Tue, Jan 27, 2026, at 12:54, Lynn wrote:

On Fri, Jan 23, 2026 at 3:17 PM Alexandre Daubois <alex.daubois+php@gmail.com> wrote:

I understand the concern about legacy code. However, if code relies on
(int) "123abc" silently returning 123, this represents a data
integrity issue that would benefit from being made explicit. The
deprecation period exists specifically to help identify and address
these cases.

Does this behavior als affect parameters being passed in a non-strict fashion? Because then I don’t see a realistic migration path within the next decade for this application.

This behavior already exists and forbids such casts, see
https://3v4l.org/WQJPv#vnull.

Thanks for your reply!

I’ve given it a bit more thought and while I 100% agree with the reasons given, I’m still afraid this change is too big of an unknown break. I do not have voting power, so all I can give is my opinion.

As an alternative, what about this? It could be the best of both worlds where the new behavior is explicit:

$var = (int) '123test'; // still works, nothing changes, becomes an `(int) 123`
int $var = '123test'; // follows the proposed rules, errors because it's not a value that's a valid integer
int $var = '123'; // works because it's a valid integer format, becomes (int) 123
int $var = (int) '123test'; // becomes (int) 123, existing behavior

This way nothing breaks and the casting remains what it is, but we can still enforce correct types. I know the 3rd line might be controversial, but it makes it an opt-in instead of opt-out feature and errors can be thrown as of day 1.

Bonus:

int|null $var = '123test'; // could result in `null` instead of throwing, similar to the Enum tryFrom.

// could enable
MyEnum $foo = '123test';
MyEnum|null $bar = '123test';

// translates to
$foo = MyEnum::from('123test');
$bar = MyEnum::tryFrom('123test');

from and tryFrom could be taken as base for custom cast functions, but that’s probably a whole different can of worms.

I kinda like this idea, but it would be nice to follow the same rules we have with function calls, thus

int|null $var = “123test”; (https://3v4l.org/C0uaK/rfc#vgit.master)

would be a TypeError. Less special cases means less bugs and less things to remember.

— Rob

On 27/01/2026 12:13, Rob Landers wrote:

I kinda like this idea, but it would be nice to follow the same rules we have with function calls, thus

int|null $var = "123test"; (Online PHP editor | rfc for C0uaK)

This comes back to my point about the two-part definition.

You are suggesting that "int|null" should only affect the *input interpretation*: "allow null as a valid input, but error on invalid input".

Lynn is suggesting "int|null" should also affect the *output behaviour*: "use null as the default output for any invalid input".

This is why I keep thinking about a *family* of cast operations, where the *input interpretation* is standardised, but the *output behaviour* is selectable by the user.

My current best thought is a generic-style syntax:

must_cast<int>( 'hello' ) // TypeError
try_cast<int>( 'hello', 0 ) // int(0)
can_cast<int>( 'hello' ) // bool(false)

must_cast<int>( null ) // TypeError
try_cast<int>( null, 0 ) // int(0)
can_cast<int>( null ) // bool(false)

must_cast<int|null>( 'hello' ) // TypeError
try_cast<int|null>( 'hello', null ) // null
can_cast<int|null>( 'hello' ) // bool(false)

must_cast<int|null>( null ) // null
try_cast<int|null>( null, null ) // null
can_cast<int|null>( null ) // bool(true)

--
Rowan Tommins
[IMSoP]