What is new in PHP 8.2
06, October 2023

While not as exciting as previous release, PHP 8.2 brings some nice addition to the language. Let's have a look at them:


true, false, null are standalone type now

While, we could use null and false with with other types as union type, from now on they can be used separately as well.

example:

#On PHP 8.1

//  union type, allowed
function bazz(): array|null
{
  return null;
}

//  union type, allowed
function bazz(): string|false
{
  return false; 
}

//  null as standalone type, not allowed
function bazz(): null
{
  return null;
}

//  false as standalone type, not allowed
function bazz(): false
{
  return false;
}

Two of the above function will trigger a error "Fatal error: Null/False can not be used as a standalone type". But with PHP 8.2, they works fine, including true:

#On PHP 8.2

function bazz(): null
{
  return null;
}

function bazz(): false
{
  return false;
}

function bazz(): true
{
  return true;
}

But why do they have been added?

  • Some of the internal php functions return null/false in case of error/failure scenario.
  • Some other php functions now always return true. In case of error/failure, they throw exceptions. So true as a type covers this scenario.

Note: true|false union type is not allowed. bool type is recommended for such use-case.



New execute_query function for mysql

To avoid SQL injection, prepared statement is always recommended for mysql query. But code for preparing SQL statements is quite verbose. We need to call three different function to execute a query and fetch the result.

<?php
$statment = $mysql->prepare("SELECT * FROM users WHERE id = ?");
$statement->execute([$id]);

foreach($statement->get_result() as $row) {
  var_dump($row);
}

But, with PHP 8.2 we can now reduce all these three function call to a single execute_query() call:

foreach($mysql->execute_query("SELECT * FROM users WHERE id = ?", [$id]) as $row) {
  var_dump($row);
}

Isn't that sweet? :)


readonly classes

On PHP 8.1, we got this awesome feature to flag class properties as "readonly" so they can be written only once in the construct. People like me really liked this feature, and used a lot in our codebase, specially for DTOs. But When we want all properties of DTOs to be readonly, it's kinda boring and pain to define each properties as readonly.

Fear not, PHP 8.2 now allows us to define whole class as "readonly". readonly classes automatically marks all of its properties as readonly. Also, dynamic properties are not allowed.

readonly class Foo
{
    public int $bar;
 
    public function __construct() {
        $this->bar = 1;
    }
}
 
$foo = new Foo();
$foo->bar = 2;
// Fatal Error: Uncaught Error: Cannot modify readonly property Foo::$bar
 
$foo->baz = 1;
// Fatal Error: Uncaught Error: Cannot create dynamic property Foo::$baz

Note:

  • all properties of readonly class must be typed
  • a readonly class can be extended by another readonly class only


dynamic class properties are now deprecated

Uhh, dynamic class properties, what a feature. I wasted so many hours of my life because of this feature. While I abused this feature a lot too, but the fact that a single typo can cause debug nightmare is unacceptable to me. I'm sure you've faced such situation too:

class User
{
  public $name;

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

$user = new User('John');

// some code

$user->nane = 'Doe'; // notice anything?

// some other code

echo $user->name;
// umm, why it prints John?

I know, modern IDE helps to prevent such scenario but there are lot of cases where dynamic properties bring more risks that the benefits.

PHP 8.2 prevent this behavior. Accessing a non-existent property on a object no longer silently creates the property. Instead it throws "Deprecated: Creation of dynamic property User::$nane is deprecated" error.

BTW, if your code uses __get/__set magic method to achieve similar effect, then you don't have to worry, these methods are not affected by this change.

But, I still need dynamic properties in my codebase

In that case you have two option:

  • Add #[AllowDynamicProperties] attribute to the class, This will enable dynamic properties feature only for that class
  • Or use \stdClass object. Dynamic properties are allowed for this object.



deprecated utf8_encode and utf8_decode

Due to some limitations specified in the RFC, using utf8_encode and utf8_decode functions will throw deprecation warning. Also, they will be removed on PHP 9.

Instead, mb_convert_encoding is recommended.


iterator function now accept arrays

functions such as iterator_to_array() and iterator_count() currently only accepts iterator as input. PHP 8.2 makes it easier by allowing us to pass regular arrays to them as well.


Hiding parameters in back traces

PHP exception stack traces are awesome. They really help a lot during debugging an error. For each trace, they contains original parameters so we can inspect it and find potential bug. But it has a drawback. Sometimes we pass sensitive information (such as password or secret keys) to functions are parameter. And these information gets written to log whenever a exception happens. Sensitive information should not be exposed like that.

To overcome this drawback, PHP 8.2 introduces new attribute called #[\SensitiveParameter]

With this, we simply add this attribute to any parameter that we think is sensitive, and inside trace log, they will be replaced by SensitiveParameterValue object.

// we don't want $password to be in stack trace
function db_connect
(
  string $host,
  #[\SensitiveParameter]
  string $password
)
{
  throw new Exception("DB connection failed");
}

db_connect('localhost', 'password');


/*
Fatal error: Uncaught Exception: DB connection failed in file.php:9
Stack trace:
#0 file.php(12): db_connect('localhost', Object(SensitiveParameterValue))
#1 {main}
  thrown in file.php on line 9
*/


New random extension

A new extension named random has been introduced. It provides a OOP interface to generate random integers. Due to its design, it is much easier to mock for testing purpose.


Constants in traits

Although I don't like this, but PHP 8.2 now supports declaring constants in traits.

trait Bar
{
  const SOMETHING = 1;
}

class Foo
{
  use Bar;
}

echo Foo::SOMETHING; //allowed

echo Bar::SOMETHING; // direct access via trait is not allowed


ini_parse_quantity function

In php.ini, there are some values that represents byte data size in various unit, such as 512K, 64M, 1G

ini_parse_quantity() function can parse these values and return bytes in integer.

ini_parse_quantity('64K'); // 65536


curl_upkeep function

Curl extension has brought us a new function to keep a curl connection alive. curl_upkeep() does this by sending small amount of traffic to the existing connection. Currently it only supports HTTP/2 connections.


imap_is_open

This new function imap_is_open() checks if a imap connection is still open or not. It accepts IMAP\Connection object and return bool.


str_split now return empty array on empty string

Before 8.2, str_split() used to return array of a single empty string if an empty string was provided as input. PHP 8.2 changes this behavior, it now return empty array

// php 8.1
str_split(""); // returns [""]

//php 8.2
str_split(""); // returns []


memory_reset_peak_usage

If you need to reset memory usage data returned by memory_get_peak_usage(). memory_reset_peak_usage() can do that.


openssl_cipher_key_length

This new openssl_cipher_key_length() function returns key length of the provided cipher algo:

openssl_cipher_key_length('AES-128-CBC'); // 16



Which of these features did you like the most? Let me know by commenting bellow.


Write comment about this article: