PHP 8 New Features: Exploring Changes and Enhancements Skip to main content

Search

Image
PHP 8 new features and changes - Part 1 - Banner

PHP 8 brings a host of new features improvements, functions, and deprecations to the language compared to PHP 7. Among all of these new features, the JIT compiler is the one sharing the limelight. However, other features like syntax changes are also to be taken into account as it is these features that will have a greater impact on the practitioners.

Since there are many important changes that we would like to talk about, so we have split this blog into several parts. This is part 1 of the series.

Issues with the old code:

With PHP 8 we should no longer consider that it will be backwards compatible as it has a pack of syntactical changes. The latest changes include:

  • The Magic quote legacy
  • The real type
  • Reflection export() methods
  • Unbinding $this from non-static closures
  • implode() parameter order mix
  • hebrevc() function
  • mb_strrpos() with encoding as 3rd argument
  • money_format() function
  • convert_cyr_string() function
  • allow_url_include in directive
  • restore_include_path() function

Let us take a look at the major PHP 8 features:

New functions

str_contains()

When trying to find out if one string is a part of another string, you will generally use str_pos() which makes use of the needle in a haystack concept. It returns an integer showing the first position at which you see the needle. When it is returning the position of a string you simply cannot check for whether or not strpos() discovered it; if it returns “0” (positions are zero-indexed and begin with 0 rather than 1), then the conditional is going to treat it as a false value, and indicating it wasn’t found. 

This means you will have to wrap it in a condition such as “strpos($haystack, $needle) !== false.” Where false indicates that it could not find the string’s position. 

To counter this, PHP 8 introduces str_contains(), which returns a simple boolean indicating if the needle is present in the haystack.

So instead of doing this:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

 

You would now do this:

if (str_contains('string with lots of words', 'words')) { /* … */ }

str_starts_with() and str_ends_with() functions:

These functions are now incorporated into the core:

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

fdiv() function

The new fdiv() function does something similar as the fmod() and intdiv() functions, which allows for division by 0. Instead of errors, you'll get INF, -INF or NAN, depending on the case

get_debug_type() function:

The function get_debug_type() returns the type of a variable. get_debug_type() returns more useful output for arrays, strings, anonymous classes and objects. Sure it sounds like gettype() but there are benefits of the later. 

For example: calling gettype() on a class \Foo\Bar would return object. Using get_debug_type() will return the class name.

get_resource_id() function:

Resources are special variables in PHP, referring to external resources. One example is a MySQL connection, another one a filehandle.

Each one of those resources gets assigned an ID, though previously the only way to know that id was to cast the resource to int:

$resourceId = (int) $resource;

 

PHP 8 adds the get_resource_id() functions, making this operation more obvious and type-safe:

$resourceId = get_resource_id($resource);

Named arguments

Named arguments allow you to pass in values to a function, by specifying the value name, so that you don't have to consider their order, and you can also skip optional parameters.

function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{ /* … */ }

foo(
    b: 'value b',
    a: 'value a',
    d: 'value d',
);

Constructor Properties

This syntactic change allows us to create data transfer objects. Instead of specifying class properties and a constructor for them, PHP can now combine them into one.

So instead of doing this:

class Money 
{
    public Currency $currency;
 
    public int $amount;
 
    public function __construct(
        Currency $currency,
        int $amount,
    ) {
        $this->currency = $currency;
        $this->amount = $amount;
    }
}

You can do this:

class Money 
{
    public function __construct(
        public Currency $currency,
        public int $amount,
    ) {}
}

Attributes allow us to declare meta-data for our functions, classes, properties and parameters. Attributes map to PHP class names (declared with an Attribute itself), and they can be fetched programmatically with PHP Reflection API.

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

This allows us to easily declare attributes/annotations which previously required storing them in doc block elements and parsing the string to infer them. 


The null safe operator

The null coalescing operator is similar to the ternary operator but will behave like an isset on the left-hand operand instead of just using its boolean value. This makes this operator especially useful for arrays and assigning defaults when a variable is not set.

It is not fully reliable as it doesn't work on method calls. Instead, you need intermediate checks, or rely on optional helpers provided by some frameworks:

$startDate = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

With the addition of the null safe operator, we can now have null coalescing-like behaviour on methods.

$dateAsString = $booking->getStartDate()?->asDateTimeString();

Union types

Union types are a collection of two or more types that indicate that either one of those can be used.

public function myfunction(Foo|Bar $input): int|float;

Note that void can never be part of a union type since it indicates "no return value at all". Furthermore, nullable unions can be written using |null, or by using the existing? notation:

public function myfunction(Foo|null $foo): void;
public function alsomyfunction(?Bar $bar): void;

JIT Compiler

PHP Opcache supports JIT. It's disabled by default, and if enabled, JIT compiles and caches native instructions. It does not make a noticeable difference in IO-bound web applications but provides a performance boost for CPU-heavy applications.

# Enabling JIT in php.ini
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing

 

Read part 2 of the blog here.