Skip to content

print_r() results in warning: unexpected NAN value was coerced to string #23662

Closed

Description

@ausi

Description

The following code:

<?php
print_r([NAN]);

Resulted in this output:

Warning: unexpected NAN value was coerced to string in /in/IW2fE on line 3
Array
(
    [0] => NAN
)

But I expected this output instead:

Array
(
    [0] => NAN
)

This warning got introduced in #19573 but I don’t think affecting print_r() was intended as this function is supposed to “Print human-readable information about a variable”.
The related functions var_dump() and var_export() are not affected by this warning.

PHP Version

PHP 8.5.8 (cli) (built: Jul  1 2026 03:46:27) (NTS)
Copyright (c) The PHP Group
Built by Homebrew
Zend Engine v4.5.8, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.8, Copyright (c), by Zend Technologies

Operating System

MacOS 26.6.2

Activity

  1. ndossche commented on Sep 11, 2026

    @ndossche
    Member

    More specifically:

    <?php
    print_r([NAN]); // warns
    var_dump(NAN); // does not warn
    var_export(NAN); // does not warn
    echo NAN; // warns

    From the RFC:

    we propose to emit the following warning when NAN is coerced (be that implicitly or explicitly) to another type:

    So based on interpretation this either means that none of the above should emit a warning, or all of them should.

    This affects passing NAN as an argument to parameters which do not declare float as a type, all casting operators, logical operators, if statements, and ternary operator.

    None of the above fall into this category, so based on this none of them should emit a warning.

    It gets more confusing because of different entry points:

    <?php
    json_encode([NAN]); // does not warn
    http_build_query(['a'=>NAN]); // warns

    This seems inconsistent: I believe they should behave the same way.

    This would fix the printing case, but leaves the json/http example inconsistent:

    diff --git a/Zend/zend.c b/Zend/zend.c
    index aa621c4daa8..c5c220aed38 100644
    --- a/Zend/zend.c
    +++ b/Zend/zend.c
    @@ -521,6 +521,10 @@ void zend_print_flat_zval_r_to_buf(smart_str *buf, zval *expr) /* {{{ */
     		case IS_STRING:
     			smart_str_append(buf, Z_STR_P(expr));
     			break;
    +		case IS_DOUBLE:
    +			/* Not going through the string coercion as printing a value is not a cast. */
    +			smart_str_append_double(buf, Z_DVAL_P(expr), (int) EG(precision), false);
    +			break;
     		default:
     		{
     			zend_string *str = zval_get_string_func(expr);
    @@ -597,6 +601,10 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
     		case IS_LONG:
     			smart_str_append_long(buf, Z_LVAL_P(expr));
     			break;
    +		case IS_DOUBLE:
    +			/* Not going through the string coercion as printing a value is not a cast. */
    +			smart_str_append_double(buf, Z_DVAL_P(expr), (int) EG(precision), false);
    +			break;
     		case IS_REFERENCE:
     			zend_print_zval_r_to_buf(buf, Z_REFVAL_P(expr), indent);
     			break;
    

    It is unclear to me what the intended semantics are, the RFC is underspecified.
    The real fix is probably to move the warning to the entry points of coercion rather than deep into the engine itself.
    cc @Girgias

  2. Girgias commented on Sep 11, 2026

    @Girgias
    Member

    TBH the intention was to warn on any coercion, including to string when priting. So the echo behaviour is correct from my PoV. Now I do agree that print_r() is more a debugging function than anything else so should probably behave like var_dump().

    The reason json_encode allows NAN is that NAN is a valid value in JSON, but it is not in HTTP.

  3. ndossche commented on Sep 11, 2026

    @ndossche
    Member

    I do find it confusing, because strictly speaking they all make a conversion to a string

    The reason json_encode allows NAN is that NAN is a valid value in JSON, but it is not in HTTP.

    This is false, NAN is not valid in JSON.

  4. Girgias commented on Sep 12, 2026

    @Girgias
    Member

    I do find it confusing, because strictly speaking they all make a conversion to a string

    sure but var_dump is a debuging utility, and var_export is meant to export code that can be executed and NAN is a valid PHP float constant. So I don't think those should warn. The question for me is print_r a debugging function or not (IMHO yes).

    The reason json_encode allows NAN is that NAN is a valid value in JSON, but it is not in HTTP.

    This is false, NAN is not valid in JSON.

    TIL, well I guess then yes should be fixed as well.

  5. ausi commented on Sep 12, 2026

    @ausi
    Author
    json_encode([NAN]); // does not warn
    

    This actually results in the error Inf and NaN cannot be JSON encoded, so there is probably no fix needed for json_encode(), see https://3v4l.org/Gfpeh#veol

  6. DanielEScherzer commented on Sep 12, 2026

    @DanielEScherzer
    Member

    Just going to cross link #22683

  7. added a commit that references this issue on Sep 13, 2026
    3b9d35a
  8. added a commit that references this issue on Sep 19, 2026
    30920e9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions