LOGO
    • d
    • n

PHP Releases | Изменеия в версиях PHP

PHP 8

8.2

8.1

PHP 8.1 — большое обновление языка PHP.

Оно содержит множество новых возможностей, включая перечисления, readonly-свойства, callback-функции как объекты первого класса, файберы, пересечение типов, улучшения производительности и многое другое.

Перечисления

RFC Документация

// PHP < 8.1
class Status
{
    const DRAFT = 'draft';
    const PUBLISHED = 'published';
    const ARCHIVED = 'archived';
}
function acceptStatus(string $status) {...}
// PHP 8.1
enum Status
{
    case Draft;
    case Published;
    case Archived;
}
function acceptStatus(Status $status) {...}

Используйте перечисления вместо набора констант, чтобы валидировать их автоматически во время выполнения кода.

Readonly-свойства

RFC Документация

// PHP < 8.1
class BlogData
{
    private Status $status;
  
    public function __construct(Status $status)
    {
        $this->status = $status;
    }
   
    public function getStatus(): Status
    {
        return $this->status;   
    }
}
// PHP 8.1
class BlogData
{
    public readonly Status $status;
  
    public function __construct(Status $status)
    {
        $this->status = $status;
    }
}

Readonly-свойства нельзя изменить после инициализации (т.е. когда им было присвоено значение). Они будут крайне полезны при реализации объектов типа VO(Объект-значени) и DTO(Объект передачи данных).

Callback-функции как объекты первого класса

RFC Документация

// PHP < 8.1
$foo = [$this, 'foo'];

$fn = Closure::fromCallable('strlen');
// PHP 8.1
$foo = $this->foo(...);

$fn = strlen(...);

С помощью нового синтаксиса любая функция может выступать в качестве объекта первого класса. Тем самым она будет рассматриваться как обычное значение, которое можно, например, сохранить в переменную.

Расширенная инициализация объектов

RFC

// PHP < 8.1
class Service
{
    private Logger $logger;

    public function __construct(
        ?Logger $logger = null,
    ) {
        $this->logger = $logger ?? new NullLogger();
    }
}
// PHP 8.1
class Service
{
    private Logger $logger;
   
    public function __construct(
        Logger $logger = new NullLogger(),
    ) {
        $this->logger = $logger;
    }
}

Объекты теперь можно использовать в качестве значений параметров по умолчанию, статических переменных и глобальных констант, а также в аргументах атрибутов.

Таким образом появилась возможность использования вложенных атрибутов.

// PHP < 8.1
class User
{
    /**
     * @Assert\All({
     *     @Assert\NotNull,
     *     @Assert\Length(min=5)
     * })
     */
    public string $name = '';
}
// PHP 8.1
class User
{
    #[\Assert\All(
        new \Assert\NotNull,
        new \Assert\Length(min: 5))
    ]
    public string $name = '';
}

Пересечение типов

RFC Документация

// PHP < 8.1
function count_and_iterate(Iterator $value) {
    if (!($value instanceof Countable)) {
        throw new TypeError('value must be Countable');
    }

    foreach ($value as $val) {
        echo $val;
    }

    count($value);
}
// PHP 8.1
function count_and_iterate(Iterator&Countable $value) {
    foreach ($value as $val) {
        echo $val;
    }

    count($value);
}

Теперь в объявлении типов параметров можно указать, что значение должно относиться к нескольким типам одновременно.

В данный момент пересечения типов нельзя использовать вместе с объединёнными типами, например, A&B|C.

Тип возвращаемого значения never

RFC Документация

// PHP < 8.1
function redirect(string $uri) {
    header('Location: ' . $uri);
    exit();
}

function redirectToLoginPage() {
    redirect('/login');
    echo 'Hello'; // <- dead code
}
// PHP 8.1
function redirect(string $uri): never {
    header('Location: ' . $uri);
    exit();
}

function redirectToLoginPage(): never {
    redirect('/login');
    echo 'Hello'; // <- dead code detected by static analysis
}

Функция или метод, объявленные с типом never, указывают на то, что они не вернут значение и либо выбросят исключение, либо завершат выполнение скрипта с помощью вызова функции die(), exit(), trigger_error() или чем-то подобным.

Окончательные константы класса

RFC Документация

//PHP < 8.1
class Foo
{
    public const XX = "foo";
}

class Bar extends Foo
{
    public const XX = "bar"; // No error
}
// PHP 8.1
class Foo
{
    final public const XX = "foo";
}

class Bar extends Foo
{
    public const XX = "bar"; // Fatal error
}

Теперь константы класса можно объявить как окончательные (final), чтобы их нельзя было переопределить в дочерних классах.

Явное восьмеричное числовое обозначение

RFC Документация

// PHP < 8.1
016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true
// PHP 8.1
0o16 === 16; // false — not confusing with explicit notation
0o16 === 14; // true

Теперь можно записывать восьмеричные числа с явным префиксом 0o.

Файберы

RFC Документация

// PHP < 8.1
$httpClient->request('https://example.com/')
        ->then(function (Response $response) {
            return $response->getBody()->buffer();
        })
        ->then(function (string $responseBody) {
            print json_decode($responseBody)['code'];
        });
// PHP 8.1
$response = $httpClient->request('https://example.com/');
print json_decode($response->getBody()->buffer())['code'];

Файберы — это примитивы для реализации облегчённой невытесняющей конкурентности. Они являются средством создания блоков кода, которые можно приостанавливать и возобновлять, как генераторы, но из любой точки стека. Файберы сами по себе не предоставляют возможностей асинхронного выполнения задач, всё равно должен быть цикл обработки событий. Однако они позволяют блокирующим и неблокирующим реализациям использовать один и тот же API.

Файберы позволяют избавиться от шаблонного кода, который ранее использовался с помощью Promise::then() или корутин на основе генератора. Библиотеки обычно создают дополнительные абстракции вокруг файберов, поэтому нет необходимости взаимодействовать с ними напрямую.

Поддержка распаковки массивов со строковыми ключами

RFC Документация

// PHP < 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = array_merge(['a' => 0], $arrayA, $arrayB);

// ['a' => 1, 'b' => 2]
// PHP 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = ['a' => 0, ...$arrayA, ...$arrayB];

// ['a' => 1, 'b' => 2]

PHP раньше поддерживал распаковку массивов с помощью оператора ..., но только если массивы были с целочисленными ключами. Теперь можно также распаковывать массивы со строковыми ключами.

Улучшения производительности

Время запроса демо-приложения Symfony 25 последовательных запусков по 250 запросов (сек) (чем меньше тем лучше)

Результат (относительно PHP 8.0):

  • Ускорение демо-приложения Symfony на 23,0%
  • Ускорение WordPress на 3,5%

Функциональность с улучшенной производительностью в PHP 8.1:

  • Бэкенд JIT для ARM64 (AArch64).
  • Кеш наследования (не требуется связывать классы при каждом запросе).
  • Ускорено разрешение имени класса (исключены преобразование регистра имени и поиск по хешу).
  • Улучшения производительности timelib и ext/date.
  • Улучшения итераторов файловой системы SPL.
  • Оптимизация функций serialize()/unserialize().
  • Оптимизация некоторых внутренних функций (get_declared_classes(), explode(), strtr(), strnatcmp(), dechex()).
  • Улучшения и исправления JIT.

Новые классы, интерфейсы и функции

  • Добавлен новый атрибут #[ReturnTypeWillChange].
  • Добавлены функции fsync и fdatasync.
  • Добавлена новая функция array_is_list.
  • Новые функции Sodium XChaCha20.

Устаревшая функциональность и изменения в обратной совместимости

  • Передача значения NULL параметрам встроенных функций, не допускающим значение NULL, объявлена устаревшей.
  • Предварительные типы возвращаемых значений во встроенных методах классов PHP
  • Интерфейс Serializable объявлен устаревшим.
  • Функции по кодированию/декодированию HTML-сущностей по умолчанию преобразуют одинарные кавычки и заменяют недопустимые символы на символ замены Юникода.
  • Ограничены способы использования переменной $GLOBALS.
  • Модуль MySQLi: режим ошибок по умолчанию установлен на выбрасывание исключения.
  • Неявное преобразование числа с плавающей точкой к целому с потерей ненулевой дробной части объявлено устаревшим.
  • Модуль finfo: ресурсы file_info заменены на объекты finfo.
  • Модуль IMAP: ресурсы imap заменены на объекты IMAP\Connection.
  • Модуль FTP: ресурсы Connection заменены на объекты FTP\Connection.
  • Модуль GD: Font identifiers заменены на объекты GdFont.
  • Модуль LDAP: ресурсы заменены на объекты LDAP\Connection, LDAP\Result и LDAP\ResultEntry.
  • Модуль PostgreSQL: ресурсы заменены на объекты PgSql\Connection, PgSql\Result и PgSql\Lob.
  • Модуль Pspell: ресурсы pspell, pspell config заменены на объекты PSpell\Dictionary, PSpell\Config.

8.0

PHP 8.0 — большое обновление языка PHP.

Оно содержит множество новых возможностей и оптимизаций, включая именованные аргументы, union type, атрибуты, упрощённое определение свойств в конструкторе, выражение match, оператор nullsafe, JIT и улучшения в системе типов, обработке ошибок и консистентности.

Именованные аргументы

RFC

// PHP7
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
// PHP8
htmlspecialchars($string, double_encode: false);
  • Указывайте только необходимые параметры, пропускайте необязательные.
  • Порядок аргументов не важен, аргументы самодокументируемы.

Атрибуты

RFC Документация

// PHP 7
class PostsController
{
    /**
     * @Route("/api/posts/{id}", methods={"GET"})
     */
    public function get($id) { /* ... */ }
}
// PHP 8
class PostsController
{
    #[Route("/api/posts/{id}", methods: ["GET"])]
    public function get($id) { /* ... */ }
}

Вместо аннотаций PHPDoc вы можете использовать структурные метаданные с нативным синтаксисом PHP.

Объявление свойств в конструкторе

RFC Документация

// PHP 7
class Point {
  public float $x;
  public float $y;
  public float $z;

  public function __construct(
    float $x = 0.0,
    float $y = 0.0,
    float $z = 0.0
  ) {
    $this->x = $x;
    $this->y = $y;
    $this->z = $z;
  }
}
// PHP 8
class Point {
  public function __construct(
    public float $x = 0.0,
    public float $y = 0.0,
    public float $z = 0.0,
  ) {}
}

Меньше шаблонного кода для определения и инициализации свойств.

Тип Union

RFC Документация

// PHP 7
class Number {
  /** @var int|float */
  private $number;

  /**
   * @param float|int $number
   */
  public function __construct($number) {
    $this->number = $number;
  }
}

new Number('NaN'); // Нет ошибки
// PHP 8
class Number {
  public function __construct(
    private int|float $number
  ) {}
}

new Number('NaN'); // TypeError

Вместо аннотаций PHPDoc для объединённых типов вы можете использовать объявления типа union, которые проверяются во время выполнения.

Выражение Match

RFC Документация

// PHP 7
switch (8.0) {
  case '8.0':
    $result = "О нет!";
    break;
  case 8.0:
    $result = "То, что я и ожидал";
    break;
}
echo $result;
//> О нет!
// PHP 8
echo match (8.0) {
  '8.0' => "О нет!",
  8.0 => "То, что я и ожидал",
};
//> То, что я и ожидал

Новое выражение match похоже на оператор switch со следующими особенностями:

  • Match — это выражение, его результат может быть сохранён в переменной или возвращён.
  • Условия match поддерживают только однострочные выражения, для которых не требуется управляющая конструкция break;.
  • Выражение match использует строгое сравнение.

Оператор Nullsafe

RFC

// PHP 7
$country =  null;

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

  if ($user !== null) {
    $address = $user->getAddress();
 
    if ($address !== null) {
      $country = $address->country;
    }
  }
}
// PHP 8
$country = $session?->user?->getAddress()?->country;

Вместо проверки на null вы можете использовать последовательность вызовов с новым оператором Nullsafe. Когда один из элементов в последовательности возвращает null, выполнение прерывается и вся последовательность возвращает null.

Улучшенное сравнение строк и чисел

RFC

// PHP 7
0 == 'foobar' // true
// PHP 8
0 == 'foobar' // false

При сравнении с числовой строкой PHP 8 использует сравнение чисел. В противном случае число преобразуется в строку и используется сравнение строк.

Ошибки согласованности типов для встроенных функций

RFC

// PHP 7
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given

array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0
// PHP 8
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given

array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0

Большинство внутренних функций теперь выбрасывают исключение Error, если при проверке параметра возникает ошибка.

Компиляция Just-In-Time

PHP 8 представляет два механизма JIT-компиляции. Трассировка JIT, наиболее перспективная из них, на синтетических бенчмарках показывает улучшение производительности примерно в 3 раза и в 1,5–2 раза на некоторых долго работающих приложениях. Стандартная производительность приложения находится на одном уровне с PHP 7.4.

Относительный вклад JIT в производительность PHP 8

Улучшения в системе типов и обработке ошибок

  • Более строгие проверки типов для арифметических/побитовых операторов RFC
  • Проверка методов абстрактных трейтов RFC
  • Правильные сигнатуры магических методов RFC
  • Реклассификация предупреждений движка RFC
  • Фатальная ошибка при несовместимости сигнатур методов RFC
  • Оператор @ больше не подавляет фатальные ошибки.
  • Наследование с private методами RFC
  • Новый тип mixed RFC
  • Возвращаемый тип static RFC
  • Типы для стандартных функций E-mail Тема
  • Непрозрачные объекты вместо ресурсов для Curl, Gd, Sockets, OpenSSL, XMLWriter, e XML расширения

Прочие улучшения синтаксиса

  • Разрешена запятая в конце списка параметров RFC и в списке use замыканий RFC
  • Блок catch без указания переменной RFC
  • Изменения синтаксиса переменных RFC
  • Имена в пространстве имен рассматриваются как единый токен RFC
  • Выражение Throw RFC
  • Добавление ::class для объектов RFC

Новые классы, интерфейсы и функции

PHP 7

7.4


7.3

7.2

7.1

7.0

PHP 5

5.6

5.5

5.4

5.3