ConFoo 2025
Released!
PHP 8.3 is a major update of the PHP language.
It contains many new features, such as explicit typing of class constants, deep-cloning of readonly properties and additions to the randomness functionality. As always it also includes performance improvements, bug fixes, and general cleanup.

Typed class constants RFC

PHP < 8.3
interface I {
// We may naively assume that the PHP constant is always a string.
const PHP = 'PHP 8.2';
}

class
Foo implements I {
// But implementing classes may define it as an array.
const PHP = [];
}
PHP 8.3
interface I {
const
string PHP = 'PHP 8.3';
}

class
Foo implements I {
const
string PHP = [];
}

// Fatal error: Cannot use array as value for class constant
// Foo::PHP of type string

Dynamic class constant fetch RFC

PHP < 8.3
class Foo {
const
PHP = 'PHP 8.2';
}

$searchableConstant = 'PHP';

var_dump(constant(Foo::class . "::{$searchableConstant}"));
PHP 8.3
class Foo {
const
PHP = 'PHP 8.3';
}

$searchableConstant = 'PHP';

var_dump(Foo::{$searchableConstant});

New #[\Override] attribute RFC

PHP < 8.3
use PHPUnit\Framework\TestCase;

final class
MyTest extends TestCase {
protected
$logFile;

protected function
setUp(): void {
$this->logFile = fopen('/tmp/logfile', 'w');
}

protected function
taerDown(): void {
fclose($this->logFile);
unlink('/tmp/logfile');
}
}

// The log file will never be removed, because the
// method name was mistyped (taerDown vs tearDown).
PHP 8.3
use PHPUnit\Framework\TestCase;

final class
MyTest extends TestCase {
protected
$logFile;

protected function
setUp(): void {
$this->logFile = fopen('/tmp/logfile', 'w');
}

#[
\Override]
protected function
taerDown(): void {
fclose($this->logFile);
unlink('/tmp/logfile');
}
}

// Fatal error: MyTest::taerDown() has #[\Override] attribute,
// but no matching parent method exists
By adding the #[\Override] attribute to a method, PHP will ensure that a method with the same name exists in a parent class or in an implemented interface. Adding the attribute makes it clear that overriding a parent method is intentional and simplifies refactoring, because the removal of an overridden parent method will be detected.

Deep-cloning of readonly properties RFC

PHP < 8.3
class PHP {
public
string $version = '8.2';
}

readonly class
Foo {
public function
__construct(
public
PHP $php
) {}

public function
__clone(): void {
$this->php = clone $this->php;
}
}

$instance = new Foo(new PHP());
$cloned = clone $instance;

// Fatal error: Cannot modify readonly property Foo::$php
PHP 8.3
class PHP {
public
string $version = '8.2';
}

readonly class
Foo {
public function
__construct(
public
PHP $php
) {}

public function
__clone(): void {
$this->php = clone $this->php;
}
}

$instance = new Foo(new PHP());
$cloned = clone $instance;

$cloned->php->version = '8.3';
readonly properties may now be modified once within the magic __clone method to enable deep-cloning of readonly properties.

New json_validate() function RFC Doc

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.3
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
json_validate() allows to check if a string is syntactically valid JSON, while being more efficient than json_decode().

New Randomizer::getBytesFromString() method RFC Doc

PHP < 8.3
// This function needs to be manually implemented.
function getBytesFromString(string $string, int $length) {
$stringLength = strlen($string);

$result = '';
for (
$i = 0; $i < $length; $i++) {
// random_int is not seedable for testing, but secure.
$result .= $string[random_int(0, $stringLength - 1)];
}

return
$result;
}

$randomDomain = sprintf(
"%s.example.com",
getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
16,
),
);

echo
$randomDomain;
PHP 8.3
// A \Random\Engine may be passed for seeding,
// the default is the secure engine.
$randomizer = new \Random\Randomizer();

$randomDomain = sprintf(
"%s.example.com",
$randomizer->getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
16,
),
);

echo
$randomDomain;
The Random Extension that was added in PHP 8.2 was extended by a new method to generate random strings consisting of specific bytes only. This method allows the developer to easily generate random identifiers, such as domain names, and numeric strings of arbitrary length.

New Randomizer::getFloat() and Randomizer::nextFloat() methods RFC Doc

PHP < 8.3
// Returns a random float between $min and $max, both including.
function getFloat(float $min, float $max) {
// This algorithm is biased for specific inputs and may
// return values outside the given range. This is impossible
// to work around in userland.
$offset = random_int(0, PHP_INT_MAX) / PHP_INT_MAX;

return
$offset * ($max - $min) + $min;
}

$temperature = getFloat(-89.2, 56.7);

$chanceForTrue = 0.1;
// getFloat(0, 1) might return the upper bound, i.e. 1,
// introducing a small bias.
$myBoolean = getFloat(0, 1) < $chanceForTrue;
PHP 8.3
$randomizer = new \Random\Randomizer();

$temperature = $randomizer->getFloat(
-
89.2,
56.7,
\Random\IntervalBoundary::ClosedClosed,
);

$chanceForTrue = 0.1;
// Randomizer::nextFloat() is equivalent to
// Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen).
// The upper bound, i.e. 1, will not be returned.
$myBoolean = $randomizer->nextFloat() < $chanceForTrue;

Due to the limited precision and implicit rounding of floating point numbers, generating an unbiased float lying within a specific interval is non-trivial and the commonly used userland solutions may generate biased results or numbers outside the requested range.

The Randomizer was also extended with two methods to generate random floats in an unbiased fashion. The Randomizer::getFloat() method uses the γ-section algorithm that was published in Drawing Random Floating-Point Numbers from an Interval. Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.

Command line linter supports multiple files PR Doc

PHP < 8.3
php -l foo.php bar.php No syntax errors detected in foo.php
PHP 8.3
php -l foo.php bar.php No syntax errors detected in foo.php No syntax errors detected in bar.php

The command line linter now accepts variadic input for filenames to lint

New Classes, Interfaces, and Functions

Deprecations and backward compatibility breaks

To Top