Что нового в PHP 7.4

Сравнение результатов теста производительности 7.4 и более старых версий PHP.

[ ...$arr ] — распаковка массива внутри массива

Wiki: Spread operator for array

Оператор ... еще называют «Splat Оператор», «Scatter operator» или «Spread operator».

Распаковка работает с версии PHP 5.6. А с выходом 7.4, мы можем использовать её в массивах.

$parts  = [ 'apple', 'pear' ];
$fruits = [ 'banana', 'orange', ...$parts, 'watermelon' ];
// [ 'banana', 'orange', 'apple', 'pear', 'watermelon' ];

Распаковывать можно несколько раз, и в отличие от распаковки аргументов, ... можно использовать где угодно. Можно добавить обычные элементы массива до и после ... оператора.

Оператор Spread работает как для обычного синтаксиса массива array(), так и для короткого [].

$arr1 = [ 1, 2, 3 ];
$arr2 = [ ...$arr1 ];    // [1, 2, 3]
$arr3 = [ 0, ...$arr1 ]; // [0, 1, 2, 3]
$arr4 = array( ...$arr1, ...$arr2, 111 ); // [1, 2, 3, 1, 2, 3, 111]
$arr5 = [ ...$arr1, ...$arr1 ]; // [1, 2, 3, 1, 2, 3]

Можно распаковать вызов функции, которая возвращает массив.

function getArr(){
	return [ 'a', 'b' ];
}
$arr6 = [ ...getArr(), 'c' ]; // ['a', 'b', 'c']

$arr7 = [ ...new ArrayIterator(['a', 'b', 'c']) ]; // ['a', 'b', 'c']

function arrGen(){
	for( $i = 11; $i < 15; $i++ ){
		yield $i;
	}
}
$arr8 = [ ...arrGen() ]; // [11, 12, 13, 14]

ВАЖНО: Строковые ключи массива не поддерживаются!

Чтобы сохранить совместимость с распаковкой аргументов, ключи в виде строк не поддерживаются. При обнаружении строкового ключа будет выброшена ошибка (recoverable error).

Распаковка по ссылке

Невозможно распаковать массив по ссылке.

$arr1 = [ 1, 2, 3] ;
$arr2 = [ ...&$arr1 ]; // ОШИБКА: неверный синтаксис

Однако если элементы в распаковываемом массиве хранятся по ссылке, то они также будут храниться по ссылке в новом массиве.

$one = 1;
$arr1 = [ & $one, 2, 3 ];
$arr2 = [ 0, ...$arr1 ];
var_dump( $arr2 );
/*
array(4) {
  [0]=>    int(0)
  [1]=>  & int(1)
  [2]=>    int(2)
  [3]=>    int(3)
}
*/
Преимущества над array_merge()
  1. Оператор Spread должен иметь лучшую производительность, чем array_merge(). Потому что он является структурой языка, а array_merge() - вызовом функции, а также для постоянных массивов может быть выполнена оптимизация времени компиляции.

  2. array_merge() поддерживает только массивы, а ... также поддерживает Traversable объекты.

    // когда получаем итераторы
    array_merge( iterator_to_array($iter1), iterator_to_array($iter2) )
    
    // когда может получить итератор или массив
    array_merge(
      is_array($iter1) ? $iter1 : iterator_to_array($iter1),
      is_array($iter2) ? $iter2 : iterator_to_array($iter2)
    )
    
    // учитываются все варианты
    [ ...$iter1, ...$iter2 ]
    

public int $id — типизация для свойств класса

Добавлена поддержка типов для свойств класса. Например:

class User {
	public int $id;
	public string $name;
}

Теперь $user->id может быть только целым числом, а $user->name могут быть присвоены только строки.

Доп информация по этой ссылке RFC: https://wiki.php.net/rfc/typed_properties_v2

fn( $x ) => $x — стрелочные функции

Добавлена поддержка стрелочных функций с неявной привязкой к области видимости по значению. Например:

$factor = 10;
$nums = array_map( fn( $num ) => $num * $factor, $nums );

В качестве еще одного примера полезности такого подхода рассмотрим вариант как писался раньше и как можно писать теперь:

function array_values_from_keys( $arr, $keys ) {
	return array_map( function ($x) use ($arr) { return $arr[$x]; }, $keys );
}

Операция с передачей параметра $arr, выполняемая замыканием, тривиальна, но она несколько теряется в синтаксисе. Стрелочные функции могут сократить эту функцию до следующей:

function array_values_from_keys( $arr, $keys ) {
	return array_map( fn( $x ) => $arr[$x], $keys );
}
Синтаксис
fn( array $x ) => $x;
fn(): int => $x;
fn( $x = 42 ) => $x;
fn( & $x ) => $x;
fn&( $x ) => $x;
fn( $x, ...$rest ) => $rest;

Подробнее на RFC: https://wiki.php.net/rfc/arrow_functions_v2

covariance & contravariance

Добавлена поддержка Ковариантности для return типа (covariance) и Контрвариантности типа аргумента (contravariance). Теперь следующий код будет работать:

class A {}
class B extends A {}

class Producer {
	public function method(): A {}
}

class ChildProducer extends Producer {
	public function method(): B {}
}

Полная поддержка дисперсии (full variance) доступна только при использовании автозагрузки (autoloading). Внутри одного файла возможны только нециклические ссылки на типы, потому что все классы должны быть доступны до того, как на них будет сделана ссылка.

Коротко о Covariance и Contravariance.

Подробнее на RFC: https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters

??= — coalesce assign оператор

Добавлена поддержка оператора coalesce assign ??=. Например:

$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';

// Теперь такую строку можно записать так
$this->request->data['comments']['user_id'] ??= 'value';

Еще пример:

if ( ! isset( $array['key'] ) ) {
	$array['key'] = computeDefault();
}

// тоже самое, но коротко
$array['key'] ??= computeDefault();

Подробнее на RFC: https://wiki.php.net/rfc/null_coalesce_equal_operator

299_792 — знак _ в числах

Добавлена поддержка разделителей подчеркивания в числовых литералах. Например:

6.674_083e-11; // float
299_792_458;   // decimal
0xCAFE_F00D;   // hexadecimal
0b0101_1111;   // binary

Подробнее RFC: https://wiki.php.net/rfc/numeric_literal_separator

WeakReference — слабые ссылки

Добавлена поддержка слабых ссылок (WeakReferences).

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

final class WeakReference {
	public static function create(object $object) : WeakReference;

	public function get() : ?object;
}

Подробнее на RFC: https://wiki.php.net/rfc/weakrefs

strip_tags( $str, ['a', 'p'] )

strip_tags() теперь также принимает массив разрешенных тегов:

// Вместо
strip_tags( $str, '<a><p>' );

// теперь можно написать
strip_tags( $str, ['a', 'p'] );

__serialize() __unserialize() — магические методы

Добавлен новый механизм для сериализации объектов, который использует два новых магических метода:

// Возвращает массив, содержащий все необходимые состояния объекта.
public function __serialize(): array;

// Восстанавливает состояние объекта из заданного массива данных.
public function __unserialize( array $data ): void;

Новый механизм сериализации заменяет интерфейс Serializable, который в будущем станет устаревшим.

array_merge() — вызов без аргументов

array_merge() и array_merge_recursive() теперь можно вызывать без аргументов, в этом случае они вернут пустой массив. Это полезно в сочетании с оператором spread, например:

array_merge( ...$arrays )

Исключения из __toString()

Теперь разрешено выбрасывать исключения из __toString(). Ранее это приводило к фатальной ошибке. Существующие recoverable фатальные ошибки при преобразовании строк были преобразованы в исключения Error.

Подробнее на RFC: https://wiki.php.net/rfc/tostring_exceptions

Эта заметка встроена в: PHP 5.3 - 8.2 — Синтаксис, Новинки