image blog php 7.3
May 5, 2020

Guide to PHP 7.3: Features, Deprecations, and Performance

PHP Development

PHP 7.3, released in December of 2018, added numerous new features and improvements to the developer experience in PHP. Alongside those improvements, it made a number of deprecations that pave the way toward PHP 8.0.

In this article, we revisit the details behind the PHP 7.3 release, including release date, end of life dates, features, notable deprecations, and performance improvements for popular web content management systems.

What Is PHP 7.3?

PHP 7.3 was the third feature release in the 7.0 series. It was released December 6, 2018. It continues to receive active support, including bugfixes, until December 6, 2020, after which it will receive security fixes only until it reaches its end-of-life on December 6, 2021.

PHP 7.3 Features

PHP 7.3 introduced a number of syntax features to make development easier, added the ability to throw an exception when parsing or encoding JSON, added support for samesite cookies, improved FastCGI Process Manager (php-fpm) logging, expanded LDAP controls, provided case-mapping and case-folding support in mbstring functions, provided some improvements to password hashing, and updated its PCRE library.

Flexible Heredoc and Nowdoc Syntax

In previous versions of PHP, “heredoc” and “nowdoc” support each had the following behavior:

  • The closing marker MUST begin on the first column of the line, and can ONLY be followed by a semicolon and/or newline
  • The closing marker CANNOT be indented.
  • The contents will be captured verbatim (except for any interpolations that occur in a heredoc).

These limitations were often somewhat infuriating for developers. The contents would necessarily require breaking normal indentation, which can make understanding class, function, and method structures where they occur more difficult.

PHP 7.3 introduces a number of changes to this behavior. First, the closing marker can now be indented. This allows it to share the indentation of the opening declaration, making it easier to see what it relates to. Additionally, the closing marker can receive additional indentation, and this extra indentation will be stripped from the contents. This is best demonstrated in an example:

class Foo
{
    public const CONTENTS = <<<’END’
        This is the contents of the constant.
        END;
}

echo Foo::CONTENTS; // “This is the contents of the constant.”

Essentially, this allows you to indent the contents of your heredoc or nowdoc and have the initial indentation removed. The result is that your heredoc and nowdoc declarations can now be indented just like any other code.

Additionally, you can now use heredoc and nowdoc declarations as either array items or function parameters, as the closing marker now no longer needs to be on a line by itself, and can be followed by any character. As an example, we could define an array as follows:

$values = [<<<’END’
    a
    b
    c
    END, ‘d e f’];

And we would now receive an array with two elements, with the first defined by the nowdoc.

Trailing Commas in Function Calls

PHP added support in the version 5 series to allow defining arrays such that the last item defined could use a trailing comma. Before that support was added, the trailing comma would implicitly append a null value to the array; after the change, no additional element would be added.

The impetus for that change was to simplify re-arrangement of array items; without the change, it was easy to accidentally add the appended null value when it was not wanted. Secondarily, it made code reviews easier: if you added another line to the end of an array declaration, previously this would be considered a two line change, as you would need to add a trailing comma to the original line, and then add the new line. With the changes, you could always append a comma to every element of an array, and adding a new element would result in a diff of one line.

This feature is used quite a bit, and many coding standards and related tools even enforce using the trailing comma. However, the usage did not extend to function arguments, which suffer many similar problems.

PHP 7.3 added support for using a trailing comma on the final argument when invoking a function or method. Note: it does NOT apply to the function or method definition!

Using References With list()

References are used when you want the actual variable, not just its value. Any modifications you make to a reference will therefore affect the original. This can be useful at times when you want to manipulate scalar values and ensure the original is also affected.

Generally, references are used when passing arguments to a function, or returning a value from a function. One context where they have never worked, however, is with the “list()” operator, which always assigned by value, never by reference. Starting in PHP 7.3, however, you can indicate that a variable captured by list dereferencing should be a reference. As an example:

$values = [‘one’, ‘two’, ‘three’];
list($a, $b, &$c) = $array;
$c .= ‘-pio’;
echo $values[2]; // “three-pio”

Counting Improvements

Arrays and classes implementing Countable can each be passed to “count()” in order to produce a count of items. If you pass a value that cannot be counted to it, you will raise an error. As such, users had to resort to ugly conditionals like the following to determine if they could count a value:

$count = 0;
if (is_array($value) || $value instanceof Countable) {
    $count = count($value);
}

PHP 7.3 added the function “is_countable()” to simplify this scenario:

$count = is_countable($value) ? count($value) : 0;

Key Access

Arrays in PHP can act either as indexed lists or as hash maps. On top of that, even with indexed lists, the keys do not necessarily need to be sequential. As such, if you want to access a specific item in an array, you need to know the key. Two keys in particular are often important: the first and the last. Prior to 7.3, you would need to do something like the following to obtain them reliably:

reset($array);
$first = key($array); // get first key
end($array);
$last = key($array); // get last key

PHP 7.3 added the “array_key_first()” and “array_key_last()” functions to simplify these operations:

$first = array_key_first($array);
$end = array_key_last($array);

JSON_THROW_ON_ERROR

Each of the “json_encode()” and “json_decode()” functions can produce errors. Prior to PHP 7.3, in order to capture an error, you needed to call “json_last_error()” after calling either function, and, if that function returned anything other than JSON_ERROR_NONE, get the associated information via “json_last_error_msg()”, and then do something with it &mdash; generally throwing an exception.

PHP 7.3 simplifies this by supplying the flag JSON_THROW_ON_ERROR. This flag can be passed via the “$options” argument of either function. When provided, the function will throw a JsonException when an error occurs.

SameSite Cookie Support

The HTTP specification added support for “same-site” cookies. These are specified via the cookie attribute “SameSite”, and can contain one of the values “Strict”, “Lax”, or “None”, with most browsers defaulting to “Lax”. When present in the cookie definition, the browser will alter when it sends the cookie to the server. Under the value “None”, it always sends the cookie. With a value of “Lax”, it will not send a cookie with cross-site subrequests (such as to load images or frames), but will still send it if the user navigates from an external site (e.g., by following a link). With a value of “Strict”, it will only send the cookie if the request originated from the same URL. This can be an important tool for preventing Cross-Site Request Forgery attacks.

Prior to PHP 7.3, the SameSite attribute was not supported by either the “setcookie()”, “setrawcookie()” or “session_set_cookie_params()” methods; in order to use it, you were forced to fallback to manually creating a Set-Cookie header via the “header()” function. Starting in PHP 7.3, each of these functions now accepts an optional array of attributes, via which they also support setting the SameSite attribute value.

Improved PHP-FPM Logging

Production users of PHP are increasingly choosing PHP-FPM, as it will work with any web server capable of FastCGI, and allows running PHP in a container separate from the web server in front of it. As such, users have extensive logging needs from the php-fpm processes. To facilitate those, PHP 7.3 added three new options. “log_limit” allows users to set the maximum length of a log line before wrapping occurs; this was previously hardcoded at 1024 characters, but now allows for more or fewer characters as needed. “log_buffering” is a new option to allow experimental logging without extra buffering. Finally, “decorate_workers_output” allows you do disable output decoration when the setting “catch_workers_output” is enabled.

LDAP Controls Support

LDAP Controls are a mechanism for specifying additional information as part of an LDAP request; as an example, they could be used to indicate the server should sort search results before returning them.

PHP 7.3 added support for providing LDAP Controls via a “$serverctrls” parameter to each function capable of sending an LDAP client query. The “ldap_parse_result()” function now also includes an optional “$serverctrls” reference that the function populates with an array of LDAP Controls returned with the response. Finally, the “ldap_get_option()” and “ldap_set_option()” functions now fully support the LDAP_OPT_SERVER_CONTROLS and LDAP_OPT_CLIENT_CONTROLS constants, to allow setting and retrieving those values via the current connection.

MBString String Case Improvements

The mbstring extension provides tools for manipulating multi-byte strings, and is used typically when a consumer needs to work with Unicode or other multi-byte formats to query or manipulate string values.

One place it has often disappointed users is around its case munging and comparison capabilities. The typical operations when using multibyte strings involve case mapping, whereby strings are converted to a specified case, and case folding, which is used to facilitate case-insensitive comparisons. Many developers mistakenly use a case mapping operation prior to performing a comparison, which can lead to false negative results that can occur due to character encoding differences.

 PHP 7.3 provided improvements around these operations. First, case insensitive operations are internally now performed using case folding techniques, making them far less strict, and leading to fewer false negative results. When using “mb_convert_case()” with the MB_CASE_TITLE option, it will now derive case via Unicode properties, leading to more accurate conversions. Additionally, the function now accepts additional option types of MB_CASE_FOLD, MB_CASE_LOWER_SIMPLE, MB_CASE_UPPER_SIMPLE, MB_CASE_TITLE_SIMPLE, and MB_CASE_FOLD_SIMPLE.

In addition to the case improvement features, the various “mb_ereg_*()” functions now support named captures (using the identical syntax to PCRE), and, further, “mb_ereg_replace()” supports using “\k<>” notation to reference named captures in replacement strings.

Argon2 Password Hash Enhancements

PHP 7.3 introduced Argon2 as an option for password hashing, providing Argon2i as an alternative to using the default bcrypt algorithm. PHP 7.3 introduced an update to the Argon2 library that exposed additional variants:

  • Argon2d maximizes resistance to GPU cracking attacks, using data-dependent memory access to provide a faster result.
  • Argon2id is a hybrid of Argon2i and Argon2d, providing the tradeoff attack protection of Argon2i with the cracking attack protection of Argon2d

Argon2id is the current recommendation from security experts. You can use the PASSWORD_ARGON2ID constant to select it when using the “password_hash()” function.

PCRE2 Migration

PHP 7.3 updated its PCRE library (which provides regular expression support) to version 2 (PCRE2). While most users should notice no effects, there are several things to note about the change:

  • The “S” modifier (indicating you want the PCRE engine to study the pattern) has no impact, as this is the default behaviour now.
  • The “X” modifier (which toggled a flag that would ignore escape characters if they were followed by letters with no special meaning when escaped) no longer has any effect, as this is now the default behaviour.
  • The Unicode engine used by PCRE was updated from Unicode 7 to Unicode 10. This could potentially change behavior of some Unicode matches.

Notable Deprecations

PHP 7.3 introduced a few deprecations, primarily to remove inconsistent behavior.

First, the “define()” function, used to declare a constant, has always accepted a third value, a Boolean flag used to indicate whether or not to allow referencing the constant case insensitively; the default is to be case sensitive. Since this is inconsistent with how constants are more generally declared (e.g., with the “const” keyword), the flag is now deprecated.

Next, the function “image2wbmp()”, for outputting the Wireless Bitmap format of an image, is deprecated in favor of the “imagewbmp()” function.

When using the “filter_var()” function to validate a URL, PHP has offered two flags, FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED. Since these have been implicitly enforced since PHP 5.2.1, PHP 7.3 deprecated their usage as they are redundant.

Finally, when using both concatenation operators (“.”) and addition operators (“+”, “-“) in the same expression, PHP 7.3 has now deprecated mixing them without surrounding the arithmetic expression. As an example, the following will now emit a deprecation notice:

echo ‘The sum is ‘ . 39 + 3;

You could fix it as follows:

echo ‘The sum is ‘ . (39 + 3);

PHP 7.3 Performance

PHP 7.3 saw the first double-digit bump in performance in the PHP 7 series, with impressive gains for a variety of applications:

  • WordPress + WooCommerce saw 12.5% gains
  • Drupal 8 saw 11.2% gains
  • Joomla saw 4.5% gains
  • Magento 2.3.0 saw 11% gains
  • Most application frameworks saw ~5% gains

Final Thoughts

PHP 7.3 brought a handful of useful features, and significant performance improvements to PHP. However, with the large number of deprecations, and the changes to the internal libraries used, developers will need to carefully test their applications when upgrading to PHP 7.3. 

    Not Ready to Upgrade?

    For teams that can't upgrade in step with the latest and greatest PHP versions, Zend offers secure, tested, and reliable PHP runtimes that can help keep your application safe while you prepare for your next upgrade. See how Zend's secure PHP runtimes can help your team today.

    See Available PHP Runtimes

    Additional Resources

    Want to keep up with what's new in PHP? The webinar below looks at the features, deprecations, and performance improvements found in PHP 7.4.

    Looking for additional reading on PHP? Be sure to visit these pages.