[PHP-DEV] [RFC] Throw ValueError for invalid characters in number base functions

Hello,

Next Friday I’ll open the vote for the RFC for Throw ValueError for invalid characters in number base functions.

RFC: https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base
Earlier RFCs:

This will throw an error for octdec, hexdec, bindec and base_convert on invalid input. We already agreed to this before (in RFC base_convert changes), so this RFC is not really controversial but more about following the right procedure.

Regards,

Sjoerd Langkemper

Hi

On 2026-08-28 10:24, Sjoerd Langkemper wrote:

Next Friday I'll open the vote for the RFC for Throw ValueError for invalid characters in number base functions.

From what I see this is the first email regarding this RFC, the archives do not show any previous emails in the discussion thread: php.internals: [RFC] Throw ValueError for invalid characters in number base functions. New RFCs require a minimum discussion period of 14 days before they may go to voting.

This will throw an error for octdec, hexdec, bindec and base_convert on invalid input. We already agreed to this before (in RFC base_convert changes), so this RFC is not really controversial but more about following the right procedure.

I'm not sure ValueError is the correct throwable to use here: I believe passing “user-provided” inputs to these methods might be an expected use case, where developers would then be interested in catching the resulting exception as an implicit validation mechanism. This is also acknowledged by the RFC:

If invalid characters are passed, a ValueError will now be thrown, which must be handled using try/catch blocks if invalid inputs are expected from untrusted sources.

The Error hierarchy is not intended to be caught, though. It should thus use something from the Exception hierarchy.

Best regards
Tim Düsterhus

On Mon, Aug 31, 2026, at 11:05, Tim Düsterhus wrote:

This will throw an error for octdec, hexdec, bindec and base_convert on
invalid input.

I’m not sure ValueError is the correct throwable to use here: I believe
passing “user-provided” inputs to these methods might be an expected use
case, where developers would then be interested in catching the
resulting exception as an implicit validation mechanism. This is also
acknowledged by the RFC: …

The Error hierarchy is not intended to be caught, though. It should thus
use something from the Exception hierarchy.

I wasn’t aware of a distinction between error and exception objects like this. Is this documented somewhere? Is there a policy when to throw errors and when to throw exceptions? Does the documentation describe that developers should only catch exceptions and not errors?

Why would these functions be used for user input more often than other functions? How can we determine whether a function should throw an exception or an error?

I think ValueError is still the right thing to throw.

On Mon, Aug 31, 2026, at 11:05, Tim Düsterhus wrote:

On 2026-08-28 10:24, Sjoerd Langkemper wrote:

Next Friday I’ll open the vote for the RFC for Throw ValueError for
invalid characters in number base functions.

From what I see this is the first email regarding this RFC

The RFC was announced here: https://news-web.php.net/php.internals/132219

And before that the same idea in another RFC: https://news-web.php.net/php.internals/131448

This gave more than a month to respond to the proposal, and exactly two weaks for responding to the exact RFC.

Regards,

Sjoerd

Hi

On 2026-08-31 15:36, Sjoerd Langkemper wrote:

The Error hierarchy is not intended to be caught, though. It should thus
use something from the Exception hierarchy.

I wasn't aware of a distinction between error and exception objects like this. Is this documented somewhere? Is there a policy when to throw errors and when to throw exceptions?

Yes, in: policies/coding-standards-and-naming.rst at main · php/policies · GitHub

Specifically:

The Error hierarchy MUST NOT be used for errors that are expected to be thrown (and caught) during normal operation of a PHP program.

As an example, a parsing function that is expected to be used with untrusted input must not throw an Error if the input is malformed. Similarly a function that interacts with the network must not throw an Error if the network operation fails. Any Error that is thrown should usually result in a reasonably obvious fix in the PHP program.

Why would these functions be used for user input more often than other functions? How can we determine whether a function should throw an exception or an error?

The quoted policy provides some examples. I'd argue that “base conversion” falls into the “parsing function” category, because it needs to parse the inputs. It is unreasonable for the user to perform a pre-validation, because that effectively means reimplementing large parts of the logic of the conversion functions.

> Next Friday I'll open the vote for the RFC for Throw ValueError for
> invalid characters in number base functions.

From what I see this is the first email regarding this RFC

The RFC was announced here: php.internals: [RFC] base_convert should throw a ValueError on characters not valid in number base

Ah. That email was a reply to an existing discussion thread though and thus didn't create a proper top-level discussion for the RFC.

This gave more than a month to respond to the proposal, and exactly two weaks for responding to the exact RFC.

Due to the above it is likely that the email was collapsed into the discussion thread without the [RFC] tag and was thus missed by mailing list participants - as was the case for me.

Best regards
Tim Düsterhus

On Mon, Aug 31, 2026, at 11:05, Tim Düsterhus wrote:

This will throw an error for octdec, hexdec, bindec and base_convert on
invalid input.

I’m not sure ValueError is the correct throwable to use here … It should thus
use something from the Exception hierarchy.

I am considering this.

Would it be InvalidCharForNumberBaseException extends Exception, or should it be more hierarchical (InvalidCharForNumberBaseException extends ValueException extends RuntimeException extends Exception)?

RangeException from SPL seems useful, but we can’t use that because it is in another extension, right?

Regards,

Sjoerd Langkemper

On 2 September 2026 12:49:35 BST, Sjoerd Langkemper <sjoerd-php@linuxonly.nl> wrote:

Would it be InvalidCharForNumberBaseException extends Exception, or should it be more hierarchical (InvalidCharForNumberBaseException extends ValueException extends RuntimeException extends Exception)?

RangeException from SPL seems useful, but we can't use that because it is in another extension, right?

Both ValueException and RuntimeException are in SPL, which is always enabled, so there's no *practical* problem using them.

However, the policy Tim linked to lays out the details of how exceptions should be defined, and says the base should be \Exception, then something extension-specific, *not* any of the SPL exceptions.

This is a slightly complex case, because there's no extension or obvious group of functions to name the base under. Possibly a BaseConversionException?

Regards,

Rowan Tommins
[IMSoP]

On 2026-08-28 20:24, Sjoerd Langkemper wrote:

Hello,

Next Friday I'll open the vote for the RFC for Throw ValueError for invalid characters in number base functions.

Just incidentally, does this list of functions include intval()?

Hi

On 9/2/26 14:12, Rowan Tommins [IMSoP] wrote:

However, the policy Tim linked to lays out the details of how exceptions should be defined, and says the base should be \Exception, then something extension-specific, *not* any of the SPL exceptions.

The policy also specifies:

Newly introduced extensions MUST follow the following rules, existing extensions SHOULD follow the rules for newly introduced exceptions, but MAY diverge for consistency with existing symbols.

and with the base conversion functions being in ext/standard, the following also applies:

Symbols MUST NOT be namespaced under the Core, Standard or Spl namespaces. Instead, these extensions should be considered as a collection of different components (str_*, password_*), and SHOULD be namespaced according to these component names.

which then means that the following:

This is a slightly complex case, because there's no extension or obvious group of functions to name the base under.

is a relevant consideration: We are adjusting existing functionality in very narrowly scoped fashion and based on the recent improvements to the stdlib, it is not too unlikely that the existing functions are completely redesigned in the future, for example by introducing a new `int\` or `number\` namespace.

> Possibly a BaseConversionException?

With the above considerations and the policy’s requirement that:

The exception message MUST NOT be the only means of distinguishing exception causes that the user might want to handle differently.

and me coming up with at least two reasonable failure cases (parsing error and out of range / loss of precision), I would probably make this a conservative change and go with the plain `\Exception` exception directly. This intentionally does not make any promises and requires the user to use “narrow try blocks” to handle the exception, which is no worse the status quo.

Best regards
Tim Düsterhus

On Fri, Sep 4, 2026, at 16:38, Tim Düsterhus wrote:

I would probably make this
a conservative change and go with the plain \Exception exception
directly.

Thanks. I changed the RFC to propose throwing an Exception instead of a ValueError: https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base

Regards,

Sjoerd

On Thu, 24 Sept 2026 at 09:50, Sjoerd Langkemper <sjoerd-php@linuxonly.nl> wrote:

On Fri, Sep 4, 2026, at 16:38, Tim Düsterhus wrote:

I would probably make this
a conservative change and go with the plain \Exception exception
directly.

Thanks. I changed the RFC to propose throwing an Exception instead of a ValueError: https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base

Regards,

Sjoerd

Hello,

throwing an Exception is in the correct direction. However, would it be possible to have some more specific exception type? \UnexpectedValueException looks like a good candidate.

Kind regards,
Lazare

On Thu, Sep 24, 2026, at 10:07, Lazare Inepologlou wrote:

On Thu, 24 Sept 2026 at 09:50, Sjoerd Langkemper <sjoerd-php@linuxonly.nl> wrote:

Thanks. I changed the RFC to propose throwing an Exception instead of a ValueError: https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base

throwing an Exception is in the correct direction. However, would it be possible to have some more specific exception type? \UnexpectedValueException looks like a good candidate.

I agree, and I think everyone agrees that throwing a more specific exception would be better eventually. However, the foundation for that is currently lacking. Throwing an Exception makes it possible to make this more specific later.

Regards,

Sjoerd

Hi

On 2026-09-24 09:46, Sjoerd Langkemper wrote:

Thanks. I changed the RFC to propose throwing an Exception instead of a ValueError: PHP: rfc:throw_error_for_invalid_characters_for_number_base

Thanks. No further remarks from my side.

Best regards
Tim Düsterhus

On 2026-09-24 19:48, Sjoerd Langkemper wrote:

Hi Morgan,

You asked a question about this RFC: PHP: rfc throw_error_for_invalid_characters_for_number_base <https:// wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base>

On Thu, Sep 3, 2026, at 13:41, Morgan wrote:

Just incidentally, does this list of functions include intval()?

I didn't respond to this as I didn't think it was an legimitate question. However, if I misunderstood you or if you have some additional thoughts about this please eloborate.

Regards,

Sjoerd

Yes, it's a legitimate question.

Will, for example, intval('hello world', 20) throw? Its current behaviour is to silently truncate at the first character that's invalid in the specified base and convert what's left. In the above example it returns 354, the value of intval('he', 20).

I note that intval() doesn't currently issue a deprecation warning, but on the other hand the function isn't mentioned at all in the earlier RFC either.

Morgan

On Thu, Sep 3, 2026, at 13:41, Morgan wrote:

Just incidentally, does this list of functions include intval()?

On Thu, Sep 24, 2026, at 14:09, Morgan wrote:

Will, for example, intval(‘hello world’, 20) throw?

It does not. The reason is technical: intval is implemented differently than base_convert and *dec. intval uses libc strtol, whereas the other function do the parsing themselves. It should probably be more strict and also throw an exception, but that is not part of this RFC.

I thought it was pretty clear that intval was not affected. Both the RFC and the top email of this thread give a list of affected functions, which does not include intval:

This will throw an error for octdec, hexdec, bindec and base_convert on invalid input.

The RFC also says explicitly that intval is not affected:

Other string-to-number or decoding functions (such as base64_decode(), intval(), etc.) are currently unaffected by this change.

Regards,

Sjoerd Langkemper

Hello list,

On Fri, Aug 28, 2026, at 10:24, Sjoerd Langkemper wrote:

RFC: https://wiki.php.net/rfc/throw_error_for_invalid_characters_for_number_base

Next Friday (16 October) I will open the vote for this RFC, which proposes to throw an exception for invalid input to base_convert, bindec, octdec, and hexdec.

Regards,

Sjoerd Langkemper