What modern PHP looks like in 2022 - A brief overview

November 28th, 2022
8 minutes read

PHP's not had the best of the reputations in the industry and a lot of it comes from people who have left PHP development long ago or just like to jump on the bandwagon. I believe the language has come a long way especially ever since PHP 8.0. In this post, I'd like to showcase some of the new features that have been added since PHP 7.4. This is not a detailed guide nor is it some kind of release notes. It's just a list of handpicked features that makes PHP as good as any other language and even better!

Table of Contents

  1. Added in PHP 7.4
    1.1. Arrow Functions
    1.2. Typed Properties

  2. Added in PHP 8.0
    2.1. Constructor Property Promotion
    2.2. Named Arguments
    2.3. Attributes
    2.4. Nullsafe Operator

  3. Added in PHP 8.1
    3.1. Enumerations
    3.2. new Keyword in Initializers

  4. Upcoming in PHP 8.2

Arrow Functions

Arrow functions are shorthand or compact ways of writing a function. It only supports single line statements and the use of return keyword can be skipped. The arrow function also has access to outer scope data without the use of the use keyword. There was an RFC to allow multi-line support for arrow functions but it was rejected.

$numbers = [ 1, 2, 3, 4, 5, 6, ];

// Before
$evenNumbers = array_filter($numbers, function ($number) {
	return $number % 2 == 0;

// After
$evenNumbers = array_filter($numbers, fn($number) => $number % 2 == 0);

Typed Properties

Prior to PHP 7.4, class properties didn't have a strict type of their own. There were workarounds like using docblocks to specify a property's type but this mostly only proved to be useful for IDEs (in autocompletion, static analysis, etc). With typed properties, you can specify the data type of a particular class property if you choose to.

class Customer {
	private string $name;
	private int $age;
	private Address $address;
	// ...

Constructor Property Promotion


A neat little feature that helps remove some boilerplate code in PHP classes. We often need to construct classes with one or more arguments defined. Normally (or pre PHP 8 rather) this would be achieved by declaring class properties, creating a constructor with arguments for these properties and then assigning the values within the constructor. With PHP 8's constructor property promotion, this can be achieved with just one step - defining the constructor arguments with their visibility modifiers and default values if necessary.

class Collection {
    public function __construct(
        public $items = []
    ) {}
    public function print() {
        echo implode(', ', $this->items);

$numbers = new Collection([1, 2, 3]);
echo $numbers->print();  // Output: 1, 2, 3

Named Arguments

If you've developed in Python or Kotlin, you're probably familiar with this feature. Named arguments lets you specify the arguments to a function call in any particular order by explicitly specifying the name of the argument being passed. Often times it's also used to make the function call more readable so the reader does not have to check the function signature to know what the arguments are. For example, we know array_map and array_filter functions, for whatever reason, had the arguments in an inconsistent order wherein the array and callback arguments were flipped in their signatures. Named arguments helps in consistently writing the arguments list. On the contrary, named arguments are not an excuse to have an inconsistent function signature.

function toCSV(array $names, string $separator = ',') {
    // ...

$names = ['John', 'Jane', 'Jacob'];

// The function can be invoked in the following ways:
toCSV(names: $names);
toCSV(names: $names, separator: '|');
toCSV(separator: '|', names: $names);



Attributes lets you define some metadata natively for a class, method, property, etc. Earlier, developers used docblocks to annotate some information and this had become a norm in many places including some frameworks and static analyzers. It worked fine in most cases but the problem was that it was not structured native code which means that there was no agreed standard or a PSR defining its usage. Attributes solve this issue by providing a native syntax to define and use them. They're the same as Java's Annotations, Python's Decorators or Rust's Attributes (with the same syntax).

// Defining an Attribute
class Route {
    public function __construct(
        public string $path,
        public string $method
    ) {}

// Usage
class UserController {
    #[Route('api/v1/users', 'GET')]
    public function index() {
    	// ...

// To fully utilize these attributes, we need additional code
// which uses the Reflection API and sets up the routes based on the attributes.

Nullsafe Operator


Null references are generally considered a bad idea (the billion dollar mistake). We tend to add multiple if statements to check if a variable is null before actually using it. This pollutes our code with multiple nested branches and makes it hard to read. In Laravel, the optional function came close to solving that issue. With nullsafe operator, these if statements can be omitted and a single line of code can help do the same. Here's the example from PHP docs:

// Before
$country =  null;

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

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

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


Enumerations or ENUMs can be thought of as a class of constants. Unlike classes, enumerations don't need to have a value assigned to them for them to be used (although they can have values, see Backed Enumerations). They can have methods just like normal classes. The differences between objects and ENUMs can be found here.

enum DaysOfWeek {
  case Monday;
  case Tuesday;
  case Wednesday;
  case Thursday;
  case Friday;
  case Saturday;
  case Sunday;

$weekends = [ DaysOfWeek::Saturday, DaysOfWeek::Sunday ];

New Keyword in Initializers


We can now have the new keyword in class constructor signature, function parameter defaults, attribute arguments, etc. It might not be a major change but it does help skipping few lines and just making the code cleaner. It just adds to syntactic sugar that constructor property promotion introduced.

class Logger {
    public function __construct(
    	public Logger $logger = new NullLogger()
    ) {}

$logger = new Logger();  // Uses NullLogger
$logger = new Logger(new FileLogger());  // Uses FileLogger

Upcoming in PHP 8.2