Dev Talks

Web Development Tutorials

PHP 8.0 to 8.4 New Features Recap
PHP

PHP 8.0 to 8.4 New Features Recap

Published on Jan 15, 2025

PHP has evolved significantly since version 8.0, introducing features that enhance performance, developer experience, and code readability. Let's say it has very important features that maybe not everyone aware of, or some features we didn't yet get used to it. This is why I thought to refresh our information and review all PHP 8 important features once again with some simple examples.



PHP 8.0


Named Arguments

PHP 8.0 introduced named arguments, allowing developers to pass values to a function by specifying the parameter name:

function process_order($id, $status, $notify = true) {
    // Implementation
}

// Using named arguments
process_order(status: "completed", id: 100, notify: false);

As mentioned in the PHP Documentation This feature allowing arguments to be order independent, self documented and optional parameters can be skipped.


Constructor Property Promotion

A more concise way to define and initialize class properties:

// Before PHP 8.0
class User {
    private string $name;
    private string $email;

    public function __construct(string $name, string $email) {
        $this->name = $name;
        $this->email = $email;
    }
}

// With PHP 8.0
class User {
    public function __construct(
        private string $name,
        private string $email,
    ) {}
}


Union Types

The ability to specify multiple allowed types for properties, parameters, and return values:

public function process(int|float $number): string|array {
    // Implementation
}


Match Expression

A more powerful and safer alternative to switch statements:

return match ($status) {
    200 => "success",
    300 => "redirect",
    400 => "not found",
    500 => "server error",
    default => "unknown status",
};


Nullsafe Operator

Safe method and property access when dealing with null values:

// Before PHP 8.0
$country =  null;

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

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

// With PHP 8.0
$country = $session?->user?->getAddress()?->country;



PHP 8.1


Enums

PHP8.1 introduced First-class support for enumerations:

// Before PHP 8.1
const STATUS_ACTIVE = 'Active';
const STATUS_INACTIVE = 'Inactive';

// With PHP 8.1
enum Status {
    case Active;
    case Inactive;
}

There is a lot we can talk about for explaining Enums for the above example this is the basic usage and it could be accessed like this:

$status = Status::Active;
echo $status->name;

// This will print Active

We could also have backed Enum when we want to represent a value from database this will be useful

// Backed Enum
enum Status: int {
    case Active = 1;
    case Inactive = 2;
}

$status = Status::Active;
echo $status->value;

// This will print 1

For Backed Enums we will have available value property alongside with the name property. If you want to explore more about Enums check it out in PHP Documentation


Readonly Properties

Properties that can only be set once, during initialization:

class User {
    public readonly string $id;
    
    public function __construct(string $id) {
        $this->id = $id;
    }
}

From PHP Documentation: "Readonly properties cannot be changed after initialization, i.e. after a value is assigned to them.
They are a great way to model value objects and data-transfer objects.
"



PHP 8.2


Readonly Classes

Entire classes can be marked as readonly:

readonly class User {
    public string $name;
    public string $email;
}

From PHP Documentation: "Marking a class as readonly will add the readonly modifier to every declared property, and prevent the creation of dynamic properties."


Null, False, and True Stand-alone Types

Allowing null, false and true as stand-alone types:

// Before PHP 8.2
function setFlag(true $value) { // Only accepts true }

// With PHP 8.2
function setFlag(bool $value) {}


Trait Constants

With PHP 8.2 you can define constants in a trait but you cannot access them through the name of the trait, but through the class that uses the trait.

trait A
{
    public const GREETING = 'hello';
}

class B
{
    use A;
}

var_dump(B::GREETING); // hello
var_dump(A::GREETING); // Error


Deprecate Dynamic Properties

I think we need to highlight this change as well, because it's now produces deprecated notice so we need to get used to the new syntax.

class User
{
    public $name;
}

$user = new User();
$user->last_name = 'Doe'; // Deprecated notice (This was okay before PHP 8.2)

$user = new stdClass();
$user->last_name = 'Doe'; // Still allowed

Unless the class opts in by using the #[\AllowDynamicProperties] otherwise we should not use dynamic properties anymore.



PHP 8.3


Typed Class Constants

Ability to specify types for class constants:

class Configuration {
    public const string DATABASE_NAME = "my_app";
    public const int MAX_CONNECTIONS = 100;
}


Dynamic Class Constant Fetch

Ability to fetch class constants dynamically:

class Config {
    public const ERROR_LEVEL = 'error';
}

$level = 'ERROR_LEVEL';
echo Config::{$level}; // outputs: error


New json_validate Function

A new function to check if string is a valid json, more efficient than json_decode().

// With PHP 8.3
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true

// Before PHP 8.3
function json_validate(string $string): bool {
    json_decode($string);

    return json_last_error() === JSON_ERROR_NONE;
}

var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true



PHP 8.4


Property Hooks

Introducing a short way of setting and getting variables that may include some modifications, this is a nice example from PHP Docs:

class User implements Named
{
    private bool $isModified = false;
 
    public function __construct(private string $first, private string $last) {}
 
    public string $fullName {
        // Override the "read" action with arbitrary logic.
        get => $this->first . " " . $this->last;
 
        // Override the "write" action with arbitrary logic.
        set { 
            [$this->first, $this->last] = explode(' ', $value, 2);
            $this->isModified = true;
        }
    }
}


New Array Functions

New functions array_find(), array_find_key(), array_any(), and array_all() are available

Those functions are discussed with more detailed examples in this article


Conclusion

In addition to the previous discussed features there are other features and improvements, but I only mentioned what caught my eye and what could be handy for a developer daily use. 

Besides also a huge improvement in performance PHP 8 introduced JIT (Just in Time) Compiler and some concurrency techniques like fibers (this may not be for end users), but in general there is been many changes in the last releases. We might have a deep look on JIT in future topic and see how it works.