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,
after: <<<'PHP'
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
PHP,
),
new FeatureComparison(
id: 'readonly_classes',
title: message('readonly_title', $lang),
description: message('readonly_description', $lang),
links: ['RFC|https://wiki.php.net/rfc/readonly_amendments'],
before: <<<'PHP'
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,
after: <<<'PHP'
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';
PHP,
),
new FeatureComparison(
id: 'json_validate',
title: message('json_validate_title', $lang),
description: message('json_validate_description', $lang),
links: [
'RFC|https://wiki.php.net/rfc/json_validate',
message('documentation', $lang) . "|/manual/$lang/function.json-validate.php",
],
before: <<<'PHP'
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,
after: <<<'PHP'
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
PHP,
),
new FeatureComparison(
id: 'randomizer_get_bytes_from_string',
title: message('randomizer_getbytesfromstring_title', $lang),
description: message('randomizer_getbytesfromstring_description', $lang),
links: [
'RFC|https://wiki.php.net/rfc/randomizer_additions#getbytesfromstring',
message('documentation', $lang) . "|/manual/$lang/random-randomizer.getbytesfromstring.php",
],
before: <<<'PHP'
// 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,
after: <<<'PHP'
// 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;
PHP,
),
new FeatureComparison(
id: 'randomizer_get_float',
title: message('randomizer_getfloat_nextfloat_title', $lang),
description: message('randomizer_getfloat_nextfloat_description', $lang),
links: [
'RFC|https://wiki.php.net/rfc/randomizer_additions#getfloat',
message('documentation', $lang) . "|/manual/$lang/random-randomizer.getfloat.php",
],
before: <<<'PHP'
// 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,
after: <<<'PHP'
$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;
PHP,
),
new FeatureComparison(
id: 'command_line_linter',
title: message('command_line_linter_title', $lang),
description: message('command_line_linter_description', $lang),
links: [
'PR|https://github.com/php/php-src/issues/10024',
message('documentation', $lang) . "|/manual/$lang/features.commandline.options.php",
],
before: <<<'CODE'
php -l foo.php bar.php
No syntax errors detected in foo.php
CODE,
after: <<<'CODE'
php -l foo.php bar.php
No syntax errors detected in foo.php
No syntax errors detected in bar.php
CODE,
highlightCode: false,
),
];
echo ReleasePage::getHeroSection(
title: message('main_title', $lang),
subtitle: message('main_subtitle', $lang),
logoSvg: <<
SVG,
upgradeNow: message('upgrade_now', $lang),
);
echo ReleasePage::getFeatureComparisons($comparisons, 'PHP 8.3', 'PHP < 8.3');
?>
= message('new_classes_title', $lang) ?>
- = message('new_dom', $lang) ?>
- = message('new_intl', $lang) ?>
- = message('new_ldap', $lang) ?>
- = message('new_mb_str_pad', $lang) ?>
- = message('new_posix', $lang) ?>
- = message('new_reflection', $lang) ?>
- = message('new_socket', $lang) ?>
- = message('new_str', $lang) ?>
- = message('new_ziparchive', $lang) ?>
- = message('new_openssl_ec', $lang) ?>
- = message('new_ini', $lang) ?>
- = message('ini_fallback', $lang) ?>
- = message('anonymous_readonly', $lang) ?>
= message('bc_title', $lang) ?>
- = message('bc_datetime', $lang) ?>
- = message('bc_arrays', $lang) ?>
- = message('bc_range', $lang) ?>
- = message('bc_traits', $lang) ?>
- = message('bc_umultipledecimalseparators', $lang) ?>
- = message('bc_mtrand', $lang) ?>
- = message('bc_reflection', $lang) ?>
- = message('bc_ini', $lang) ?>
- = message('bc_standard', $lang) ?>
- = message('bc_sqlite3', $lang) ?>
false]);