¡Lanzado!

PHP 8.3 es una actualización importante del lenguaje PHP.
Contiene muchas características nuevas, como la tipificación explícita de constantes de clase, la clonación profunda de propiedades de solo lectura y adiciones a la funcionalidad de aleatoriedad. Como siempre, también incluye mejoras de rendimiento, correcciones de errores y limpieza general.

Constantes de clase tipificadas

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 RFC
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

Búsqueda dinámica de constantes de clase

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

$searchableConstant = 'PHP';

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

$searchableConstant = 'PHP';

var_dump(Foo::{$searchableConstant});

Nuevo atributo #[\Override]

Al agregar el atributo #[\Override] a un método, PHP asegurará que un método con el mismo nombre exista en una clase padre o en una interfaz implementada. Agregar el atributo aclara que la sobreescritura de un método padre es intencional y simplifica la refactorización, porque la eliminación de un método padre sobreescrito será detectada.

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 RFC
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

Clonación profunda de propiedades de solo lectura

readonly las propiedades ahora pueden ser modificadas una vez dentro del método mágico __clone para permitir la clonación profunda de propiedades de solo lectura.

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 RFC
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';

Nueva función json_validate()

json_validate() permite verificar si una cadena es JSON sintácticamente válido, siendo más eficiente que json_decode().

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
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true

Nuevo método Randomizer::getBytesFromString()

La Extensión Aleatoria que se agregó en PHP 8.2 se extendió con un nuevo método para generar cadenas aleatorias compuestas solo por bytes específicos. Este método permite al desarrollador generar fácilmente identificadores aleatorios, como nombres de dominio y cadenas numéricas de longitud arbitraria.

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;
// 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;

Nuevos métodos Randomizer::getFloat() y Randomizer::nextFloat()

Debido a la precisión limitada y el redondeo implícito de los números de punto flotante, generar un flotante imparcial dentro de un intervalo específico no es trivial y las soluciones comunes de usuario pueden generar resultados sesgados o números fuera del rango solicitado.

La Aleatorizadora también se extendió con dos métodos para generar flotantes aleatorios de manera imparcial. El método Randomizer::getFloat() usa el algoritmo de sección γ que se publicó en Drawing Random Floating-Point Numbers from an Interval. Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.

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;
$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;

El linter de línea de comandos admite múltiples archivos

El linter de línea de comandos ahora acepta entrada variada para nombres de archivos a revisar

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

Nuevas Clases, Interfaces y Funciones

Deprecaciones y rupturas de compatibilidad hacia atrás

To Top