Too Many Good Features In PHP 8 (You may not be aware of)

PHP 8 was released at the end of November 2020! It is one of the world’s most popular programming language, used by web developers around the globe.  With PHP 8, there are a number of new features that make it a great fit for large-scale enterprise applications, including web services and micro-services, and applications that run on the cloud.  

The language also supports object-oriented programming, dynamic typing, and functional programming, which opens up new possibilities for developers. PHP 8 will be much faster than PHP 7 because of the new asynchronous design and JIT compiler, you will be able to build asynchronous applications in PHP 8 which is a big deal for websites.

We'll cover the following topics in this blog.

  1. Could we upgrade to PHP 8 straight away?
  2. PHP 8 and it's Breaking Changes
  3. New Functions, Classes and Interfaces in PHP 8
  4. Deprecated Features in PHP 8

Could We Upgrade To PHP 8 Straight Away?

Many people are discussing whether it would be feasible to upgrade straight away. Before upgrading to PHP 8, of course, you should do your due diligence, to check whether in general everything in your application is compatible which were working with your PHP version. The best way to migrate to any newer version is, if you also look through the migration guide that should give you a quick answer to some of your compatibility questions.

Some have become outdated which may not work after upgrading to PHP 8, if so, then it's a big NO till those are resolved. Issues may also arise due to dependencies and therefore you should verify your dependencies in composer.json whether it supports PHP 8 or not.

Some functions and features that were deprecated in PHP 7.x and have been completely removed. These are:

  • The $php_errormsg variable
  • The create_function() function
  • The mbstring.func_overload ini directive
  • The real type
  • The allow_url_include ini directive
  • The restore_include_path() function
  • The each() function

A Quality Assurance team should do regression testing after updating any new feature to ensure everything works perfectly as expected in the current version of your application, through which you can also be able to identify new bugs right away and can be fixed as soon as possible.

Don't Update Without  Proper Testing

Once the testing process is completed you can update your newer version in the production. Always measure the performance of your older version and have insights so that you can compare it with the newer version's performance and get to know which setup will be optimal for your system.

Thus, you can update your system by performing all the tests and ensure that everything is safe before releasing it in production.

PHP 8 And Its Breaking Changes

The team has packed some big changes into the new iteration of the standard. As always, there's a lot of new features and changes baked into the new version, so we're going to take a look at some the most important ones. First up, we have the addition of a new standard library, which means an update of the core to version 8.0.0. This opens the door to many new and exciting possibilities. So, let's take a gander at what's changed!

PHP 8.0 comes with numerous improvements and new features such as:

  1. Union Types
  2. Just In Time Compilation
  3. Named Arguments
  4. Match Expressions
  5. Attributes
  6. Constructor Property Promotion
  7. Nullsafe Operator
  8. Weak Maps
  9. Saner string to number comparisons
  10. Consistent type errors for internal functions

#1 Union Types

PHP has shown some good progress in types. Prior to PHP version 8.0 you could only declare a single type for properties, parameters, and return types. Union Type is a user-defined datatype which is a collection of all variables of different datatypes in the same memory location. To separate each datatype we use a vertical bar( | ) here.

Ex: String | Boolean | Number

class addition
{
    private int|float $no_1;

    public function add(float|int $no_2): int|float
    {
        return $no_2 + $no_1;
    }
}

There are handful of exceptions that can't be used together such as:

string | void - Since void type is allowed only as a return type and therefore it is not allowed to combine with any other datatype.

To declare a certain property as string or null, you can declare it as ?string. string | null and ?string both notations are same but those two can't be blended together. More that one type can be declared while using null.

data_type1 | data_type2 | null

Union Type doesn't allow duplicate or redundant types in type declaration. These are checked at the compile time and will return in a syntax error.

Fatal error: Duplicate type ... is redundant in ... on line ...

#2 Just In Time Compilation

JIT in PHP 8 is a new extension for PHP 8 which lets you create JIT (Just In Time) compiled code within PHP. It is written in PHP and uses the Zend Engine II as a runtime. JIT-PHP 8's goal is to offer high performance for PHP applications by compiling the generated code into native machine code during runtime.

Relative JIT contribution to PHP 8 performance

Image Source

JIT'd code runs fast and uses less memory, and it is much faster to load, so JIT is commonly used in complex web applications and even on mobile devices. JIT in PHP 8 makes it easy to create JIT'd code in PHP 8, and it offers a few different types of JIT to use, including JIT compiled PHP, JIT compiled JavaScript (called JITJS) and JIT compiled C++.

#3 Named Arguments

PHP 8 has a new feature that allows you to specify the name of the argument in the method signature, which is useful when you have a lot of arguments in a method and you want to maintain the order of the arguments in your code. This causes the arguments order-independent, and allows skipping default values arbitrarily.

class addition
{
    public function add($no_1: 10, $no_2: 50):
    {
        return $no_2 + $no_1;
    }
}

Named arguments are more useful when you want to increase the readability of your code.

#4 Match Expressions

Most programming languages have some form of expression matching. Indeed, PHP 7 has already had three different versions of expression matching built in over its lifetime: the preg_match function, the ereg functions (match andereg) and the eregi family of functions.

$result = match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};
 
echo $result

Up until the release of PHP 7, all expression matching in PHP has been done purely in the interpreter. This meant that if the expression didn't fit in the machine's instruction cache, the performance was terrible. In PHP 8, matching expressions are compiled to a native opcode. As a result, performance is now linear in the size of the expression.

#5 Attributes

One of the most notable changes in PHP 8 is the addition of attributes. Attributes are used to decorate methods and functions, and have specific effects on the function. Attributes let you add metadata to your functions, classes and variables. This metadata can define the purpose of the function, the parameters it takes and more, making your code easier to read and understand.

#[Custom Attribute]
class Foo {
    #[AnotherAttribute(42)]
    public function bar(): void {}
}

Properties are a way of making your code more self-documenting, and also more robust. They're a simple way to let other coders know what kind of data you're expecting, and to ensure that they're providing the right kind of information.

#6 Constructor Property Promotion

One of these changes, constructor property promotion, is about making property getters and setters optional. In PHP 7, if you want to have a method access a property in a class, you have to use the public keyword, like so:

<?php 
class ExampleClass { 
    public $foo; 
    } 

$example = new ExampleClass(); $example->foo = "bar"; 

However, PHP 8 will have the ability to make this optional. If the method is a member of the class, it can access the property without having to use the public keyword.

#7 Nullsafe Operator

One of the most significant changes is the nullsafe operator. The nullsafe operator helps to prevent a Null Reference Exception. If you've ever had to write code to check if a variable was set before you could use it, or had to deal with the infamous null pointer, then Nullsafe Operator in PHP 8 will be a welcome new addition to the language.

$country =  null;

if ($session !== null) {
  $user = $session->user;

  if ($user !== null) {
    $address = $user->getAddress();
 
    if ($address !== null) {
      $country = $address->country;
    }
  }
}

The above PHP 7 code can be rewritten as below in PHP 8:

$country = $session?->user?->getAddress()?->country;

With the new nullsafe operator you can use a chain of calls instead of null check conditions.

Nullable types were introduced in PHP 7, so that a user could declare a field as "in optional" at the same time. Nullsafe operator is an operator, which allows you to check if the code can be executed without errors.

#8 Weak Maps

As you may have guessed from the name, weak maps are maps that allow you to associate data with a key, but let you access that data if and only if the key is in use somewhere else in your code. They are called "weak" because the garbage collector can release them if there is not a reference to them from another object. This makes it so you never have to worry about memory leaks when using weak maps.

Example:

$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);

In long-run processes, this can prevent memory leaks, which eventually improve performance.

#9 Saner string to number comparisons

There are two comparisons in PHP - Strict comparisons === and !== and the Non-strict comparisons ==, !=, >, >=, <, <= and <=>. Comparison between the string and number using the non-strict comparison operators work by casting.

For example:

0 == "austin"  // returns true

#10 Consistent type errors for internal functions

PHP will already throw TypeError in User-defined functions,  but internal functions rather emitted warnings and returned null. As of PHP 8 the behaviour of internal functions have been made consistent.

strlen([]); // Warning: strlen() expects parameter 1 to be 
string, array given

array_chunk([], -1); // Warning: array_chunk(): Size parameter 
expected to be greater than 0

In PHP 8 the error description is in a detailed way which can be easily understood by everyone.

strlen([]); // TypeError: strlen(): Argument #1 ($str) must 
be of type string, array given

array_chunk([], -1); // ValueError: array_chunk(): Argument #2 
($length) must be greater than 0

New Functions, Classes, Interfaces in PHP 8

With PHP 8, comes a slew of new features and functions. We've already covered some of the new features, but here we have focused on the new classes, interfaces and methods that will become a part of your everyday PHP experience.

  1. str_contains()
  2. str_starts_with() and str_ends_with()
  3. preg_last_error_msg()
  4. get_debug_type()
  5. get_resource_id()
  6. Stringable interface

a.) str_contains()

Helps you in determining whether the given sub-string is present inside the string.

Example :

str_contains ( string $austin , string $tin ) : bool

Executes a case-sensitive operations and checks whether the string austin contains the substring tin.

b.) string_starts_with and str_ends_with()

Using this function you can easily find whether the given sub-string is at the beginning or ending of the string.

Example:

str_starts_with(string $austin, string $au): bool;


str_ends_with(string $austin, string $tin): bool;

These new functions can be impersonated with strpos, substr, strncmp, and substr_compare. However, the new functions were favourably received due to its engine-level optimizations and their regular use cases.

c.) preg_last_error_msg()

If something went wrong in PHP's perg_ function it doesn't throw exceptions. If the caller wants to retrieve any error messages he/she can use preg_last_error function, which will return error codes.

This new preg_last_error_msg returns an error message or "No Error" if there is no errors in your code.

Example:

preg_match('/(?:\D+|<\d+>)*[!?]/', 'Brayden Austin Diego');
var_dump(preg_last_error_msg()); // Backtrack limit was exhausted

This can be polyfilled with a hardcoded list of error codes mapped to error messages:

if (!function_exists('preg_last_error_msg')) {
    /**
    * Returns the error message of the last PCRE regex execution.
    * @return string
    */
    function preg_last_error_msg(): string {
        switch (preg_last_error()) {
            case PREG_NO_ERROR:
                return 'No error';
            case PREG_INTERNAL_ERROR:
                return 'Internal error';
            case PREG_BACKTRACK_LIMIT_ERROR:
                return 'Backtrack limit exhausted';
            case PREG_RECURSION_LIMIT_ERROR:
                return 'Recursion limit exhausted';
            case PREG_BAD_UTF8_ERROR:
                return 'Malformed UTF-8 characters, possibly incorrectly encoded';
            case PREG_BAD_UTF8_OFFSET_ERROR:
                return 'The offset did not correspond to the beginning of a valid UTF-8 code point';
            case PREG_JIT_STACKLIMIT_ERROR:
                return 'JIT stack limit exhausted';

            default:
                return 'Unknown error';
        }
    }
}

d.) get_debug_type()

PHP already have a function get_type() which returns the type of the varibale and in PHP 8 has introduced a new function get_dubug_type() which returns the native type and also resolves the class names.

Example:

$float = 1.2;

gettype($float);
// "double"
$float = 1.2;

get_debug_type($float);
// "float"

For historical reasons, gettype() returns double for float type. get_debug_type() returns the right type name.

e.) get_resource_id()

PHP resources, such as Curl handlers, open files, database connections, can be cast to int. PHP 8 adds a new get_resource_id function that is essentially a (int) $resource cast to make it easier to retrieve the resource ID.

function get_resource_id($res): int {}

The advantage of the new get_resource_id function is the type safety, that arguments and return type are checked to be a resource and an int.

f.) Stringable Interface

The new Stringable interfaces gets automatically added once a class implements the __toString method. There is no need to explicitly implement this interface.

interface Stringable {
    public function __toString(): string;
}

It has two goals:

  1. allow using string|Stringable to express string|object-with-__toString()
  2. provide a forward upgrade path from PHP 7 to 8

Deprecated Features In PHP 8

Not to be out-shined, the new version will also include the removal of some features that are now deprecated. Recently the team have been deprecated many features. Not only does this have a major impact on your current applications, but future projects should also be considered.

Here I have listed some of the features that have been deprecated and removed in PHP version 8.

  1. $php_errrormsg
  2. create_function()
  3. parse_str()
  4. gmp_random()
  5. each()
  6. $errcontext() argument of error handler

i.) $php_errormsg()

It contains the text of the last error message generated by PHP. It is available only within the scope in which the error occurred and only if the track_error configuration is enabled.

You can use error_get_last() instead of $php_errormsg().

Example:

<?php
echo $a;
print_r(error_get_last());
?>

Output:

Array
(
    [type] => 8
    [message] => Undefined variable: a
    [file] => C:\WWW\index.php
    [line] => 2
)

ii.) create_function()

This is an inbuilt function which has been used to create anonymous(lambda-style) function in PHP. This has been deprecated in PHP 7.2.0 and removed in PHP version 8. Apart from potential source of security issues it also has very poor performance and memory usage characteristics.

Example:

<?php
 
$func = create_function('$a,$b', 'return $a * $b;');
echo $func(1, 4);
?>

There are two  arguments The above code can be rewritten with the native anonymous function as below.

<?php
 
$func = function (int $a, int $b) {
    return $a * $b;
};
echo $func(1, 4);
?>

iii.) parse_str()

Parses the query string into variables. The string passed to this function for parsing is in the format of a query string passed via a URL.

The second parameter $result has been discouraged and deprecated from PHP version 7.2

parse_str ( string $string , array &$result ) : void

iv.) gmp_random()

This function generates a random number between 0 and (2**n) - 1.

n - number of bits per limb multiplied by limiter. If the limiter is negative, a negative number will be generated.

The number of bits in the limb is not static and can vary from system to system.

Example:

<?php
$rand1 = gmp_random(1);
echo gmp_strval($rand1)
?>

Output:

1915834968

You can use gmp_random_bits() or gmp_random_range() instead of gmp_random() in PHP version 8.

v.) each()

It is used with list() to traverse an array. It returns the current key and value pair from the array. Since this function is highly discouraged the team has deprecated and removed it from PHP version 8.

vi.) $errcontext() argument of error handler

This is used as a fifth parameter which is optional in error_handler() function. It is an array that points to the active symbol table at the point when an error occurred. This parameter has been deprecated from PHP 7.2.0, and removed from PHP version 8.

If your function defines this parameter without a default, an error of "too few arguments" will be raised when it is called.

Final Thoughts

In the above post we have covered the new features and improvements introduced in PHP 8. On the whole PHP 8 offers major improvements which will be more useful for all the PHP developers out there.

Just In Time (JIT) is the most expected feature and in addition they have also improvised type systems which are brilliant. Test the new features introduced by PHP 8 and start upgrading. Before heading towards testing the new features take a look into the official documentation.

Before migrating the entire application get a deep understanding from migration guide on the PHP official documentation. Decide the way you wish to proceed with the performance and growth of your business.