What's new and changing in PHP 7.3

Tue, 2018-04-17 23:46

This is a live document (until PHP 7.3 is released as generally available) on changes and new features to expect in PHP 7.3, with code examples, relevant RFCs, and the rationale behind them, in their chronological order.

  1. Heredoc and Nowdoc syntax requirements are more relaxed
  2. Allow trailing comma in function and method calls
  3. Option to make json_encode and json_decode throw exceptions on errors
  4. References in list()
  5. Introduced is_countable() function

Heredoc and Nowdoc syntax requirements are more relaxed

Heredoc and Nowdoc syntax, that helped to use multi-line strings had rigid requirements that the ending identifier should be the first string appearing in a new line.

For example:

$foo = <<<IDENTIFIER
the crazy dog jumps over the lazy fox
"foo" bar;

In here, the last IDENTIFIER must be the first string in a new line for this to work. In addition, there must not be any other characters after the last IDENTIFIER (other than a semi colon, which is optional).

The RFC for PHP 7.3 suggested to remove the above requirement with the goal of making the code more readable. Before this RFC, one had to break the indentation used in the rest of the code just so here/now doc tokens can be used.

The RFC suggests making these changes to heredoc/nowdoc syntax:

  1. The ending token no longer needs to be the first string of the line.
  2. The ending token can be indented with spaces or tabs
  3. The white-space characters (space or tab) must not be intermixed. If you do so, you will get a Parse error: Invalid indentation - tabs and spaces cannot be mixed in .. on line ...
  4. The exact number of spaces/tabs used in the ending token will be stripped off from the contents within the heredoc/nowdoc expression.
  5. If the number of white-space characters used in the ending token is greater than any of the white-space characters within the expression, you will get Parse error: Invalid body indentation level (expecting an indentation level of at least ..) in .. on line ..
  6. You can add more expressions after the ending token without any errors
  7. Here is an example snippet that takes advantage of this new feature without violating the newly enforced rules:

    $foo = ['foo', 'bar', <<<EOT
        -  hello world! --
      EOT, 'qux', 'quux'


    the output would be:

    array(5) {        
      string(3) "foo" 
      string(3) "bar" 
      string(29) "baz 
      -  hello world! --
      string(3) "qux"
      string(4) "quux"

    Notice how the white-spaces used in the heredoc declaration did not make in to the var_dump()'d output, and we continued to add more element to the $foo array after the EOT token.

    RFC, Externals.io discussion, Implementation

    Backwards compatibility impact
    As long as you don't have any heredox/nowdoc string litrals that contain the same token as the first positive character in a line, you are golden.

    $foo = <<<HELLO
      HELLO_WORLD <-- this will not terminate the string literal
      HELLOWORLD <-- this one will not either.
      HELLO WORLD<-- this one will

    If you have any heredoc/nowdoc syntax similar to the above, note that with PHP 7.3, PHP assumes the HELLO terminates the string literal, and will throw an error on the next line. In earlier versions, the HELLO WORLD is not considered the ending token of the heredoc. Thanks to /u/ImSuperObjective2 on reddit for pointing this out.

    Allow trailing comma in function and method calls

    This is a simple change, which suggests allowing trailing commas in function and method calls. This does not affect declarations.

    For example, the following syntax would be allowed:

    // regular functions.
    foo('bar', 'baz',); // Notice the trailing comma after 'baz'.

    In pre-PHP-7.3, the snippet above throws an error PHP Parse error:  syntax error, unexpected ')' in .. on line ..

    You cannot use more than one commas at the end or use commas to skip arguments - the advantage of this change is mainly for those functions with variadic parameters. This change also makes the array syntax (which allows trailing commas already) consistent.

    Note that you cannot use this in function/method declarations; this is wrong:

    function foo($bar, $baz, ) { // nah, you can't do this.

    RFC, Externals.io discussion, Implementation

    Backwards compatibility impact
    None - Your existing code will continue to work. If you have any function calls that accept variadic parameters and believe that you could make diffs cleaner with this, I'd suggest you go ahead and add trailing commas to the calls. If you put a trailing comma for every function call, you are clearly overdoing it.

    Option to make json_encode and json_decode throw exceptions on errors

    This is one of my favorites. For all these years, json_encode() and json_decode() were silent about errors in the provided PHP variables or the JSON string. This was prone to buggy code because not everyone knows this edge case. This was even criticized in the famous PHP: A Fractal bad design post.

    json_decode returns null for invalid input, even though null is also a perfectly valid object for JSON to decode to—this function is completely unreliable unless you also call json_last_error every time you use it.

    It took us 6 years since that blog post, but we now have an option to make PHP throw an error on JSON operation failures:

    try {
      json_decode("{", false, 512, JSON_THROW_ON_ERROR);
    catch (\JsonException $exception) {
      echo $exception->getMessage(); // echoes "Syntax error"

    The new \JsonException is a subclass of \Exception, and both JSON_THROW_ON_ERROR constant and JsonException exception are declared in the global namespace.

    I highly recommend you start to use this feature. There are contributed libraries, such as daverandom/exceptional-json that brought similar functionality until PHP 7.2. With this feature now in PHP core, you can remove this package or tons of ugly boilerplate code calling json_last_error every-time you make a JSON operation.

    RFC, Externals.io discussion, Implementation

    Backwards compatibility impact
    None, unless you have declared your own exception and/or constants with conflicting names.

    References in list()

    The list() is useful to quickly assign a variable from an array of variables. Until PHP 7.3, is was not possible to assign the variables by reference. Prior to PHP 7.3, the following snippet would throw a fatal error:

    $arr = ['apple', 'orange'];
    list($a, &$b) = $arr;
    $b = 'banana';
    echo $arr[1];
    // Fatal error: [] and list() assignments cannot be by reference in .. on line ..

    With PHP 7.3, you will be able do so, and the output from echo $arr[1]; will be "banana"! Imagine happy minions from Despicable Me! The [$a, &$b] = $arr syntax will get this feature too of course.

    You still cannot reference non-referencable variables: list($a, &$b) = [12, 14]; will throw Fatal error: Cannot assign reference to non referencable value in .. on line ..

    Minor nitpick: while "referencable" is an acceptable spelling, "referenceable" is used a lot more widely.

    RFC, Externals.io discussion, Implementation

    Backwards compatibility impact
    None. Instead of using list() assignment to populate multiple variables, I would suggest you to use value objects to make things cleaner. They will be passed by reference anyway and will make your code much cleaner.

    Introduced is_countable() function

    PHP 7.2 deprecated quite a lot of functions and buggy use cases. In PHP 7.2, if you call count() on a variable that is not "countable", PHP shows a warning about it. A common fix was to check if the given variable is "countable" before calling count() on it.

    A "countable" variable is either an array, or an object whose class implements \Countable interface. Because there can be a lot of boilerplate code, PHP 7.3 now has new is_countable() function that returns if the passed variable is... well... countable.

    I have put together a polyfill for is_countable() if you want to use this on pre-PHP 7.3 code.

    RFC, Externals.io discussion, Implementation

    Backwards compatibility impact
    Unless you have declared your own is_countable function, there shouldn't be any issues.