PHP 5.3 – 7.4 – Синтаксис, Новинки
У цій статті я розгляну цікаві моменти у розвитку синтаксису PHP – вийде свого роду список змін PHP . Що з’явилося у версіях PHP 5.3 та вище. Основна мета цієї статті – створити якусь карту змін синтаксису, що можна було швидко освіжити в пам’яті «фішки» синтаксису PHP, які спрощують коддинг. У мене останнім часом часто виникає необхідність переконатися, що можна в PHP 5.3, а чого не можна.
При написанні плагінів або тим WordPress можна використовувати тільки можливості PHP 5.3, мабуть, не варто… Все що можна в PHP 5.4 і вище, потрібно вирішувати через створення допоміжних змінних. Втім, навіть 5.3 не завжди працює, є ще сервери з PHP 5.2, але добре, що це рідкість. До речі, сам WordPress ще підтримує PHP 5.2.
Отже, до новинок…
PHP 5.3
В PHP 5.3, как и во всей пятой ветке PHP, включена новая машина-интерпретатор скриптов Zend Engine 2.0. Благодаря этому PHP стал работать быстрее примерно на 15-20%.
- Wiki: PHP 5.3
- php.net: Новые возможности в PHP 5.3.
?:
— сокращение тернарного оператора
С PHP 5.3 стало возможным не писать среднюю часть тернарного оператора. Выражение expr1 ?: expr3
возвращает expr1 если expr1 не пустой, и expr3 в противном случае.
Тернарный — состоящий из трёх частей, компонентов.
$a = $expr1 ?: $expr3; // равносильно записи: $a = $expr1 ? $expr1 : $expr3;
Пример тернарного оператора:
// полная запись if ( $a > 100 ) $result = "Больше"; else $result = "Меньше"; // краткая запись $result = $a > 100 ? "Больше" : "Меньше";
В короткой записи есть еще момент производительности, например:
// полная запись if ( get_post_meta(25, 'meta_key', 1) ) echo esc_html( get_post_meta(25, 'meta_key', 1) ); else echo 'Мета поля нет'; // короткая запись echo esc_html( get_post_meta(25, 'meta_key', 1) ?: 'Мета поля нет' );
В полной записи функция get_post_meta() вызывается 2 раза. В короткой один раз, и если она что-то вернула, второму аргументу тернарного оператора сразу передается полученное значение: не нужны дополнительные переменные…
$func = function() use (){ }
— анонимные (лямбда) функции
Лямбда-функции еще называют «анонимными функциями», потому что для них не указывается название.
Лямбда-функции представляют собой замыкание (англ. closure) — это особый вид функции, которая определена в теле другой функции и создаётся каждый раз во время её выполнения. Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. Насколько я понял, любая функция — это замыкание текущего контекста, т.е. контекст не будет очищен пока работает функция. Но если в функции есть лямбда-функция, то она становится замыканием, и если в неё передаются переменные из «верхней» функции, то они не будут очищены, до тех пор пока работает вложенная-функция, даже если «верхняя» функция работу закончила…
В ранних версиях, анонимные функции создавались с помощью функции create_function().
Пример создания анонимной функции для сортировки usort():
$arr = array(3, 2, 5, 6, 1); usort( $arr, function($a, $b) { if ( $a == $b ) return 0; return ( $a > $b ) ? -1 : 1; });
Еще одна фишка лямбда-функций — это использование переменных из текущей области видимости, с помощью оператора use
:
$var = 'Превед, Медвед!'; $func = function() use ( $var ) { echo $var; }; $func(); //> Превед, Медвед!
Переменные передаются как значение, но можно передать и ссылку на переменную, указав &
:
$var = 'Превед, Медвед!'; $func = function() use ( & $var ) { $var = $var .' Мы в гости!'; }; $func(); // вызовем echo $var; //> Превед, Медвед! Мы в гости!
method()->var
— получение объекта из метода/функции
Это удобно:
$object->method()->method()->method(); $object->method()->method()->member = 5;
В PHP ниже 5.3 писали как-то так:
$tmp = & $object->method(); $tmp = & $tmp->method(); $tmp->method();
<<<'DOC'
— поддержка NOWDOC
В php 5.3 можно использовать аналог HEREDOC, который называется NOWDOC. Особенность его в том, что внутри него переменные остаются простым текстом, как если бы мы указали её в строке с одинарными кавычками: 'текст $foo'
:
$foo = 'Лето'; // HEREDOC был в 5.2 $str = <<<DOC Текст с переменной '$foo' DOC; echo $str; // Текст с переменной 'Лето' // NOWDOC появился в 5.3 $str = <<<'DOC' Текст с переменной '. $foo .' DOC; echo $str; // Текст с переменной '. $foo .'
namespace
— поддержка пространств имен
Пространства имен нужны, чтобы избежать конфликтов при совпадении названий функций/классов/переменных/констант. Если коротко: одинаковые называния в разных пространствах — это разные названия.
Пример ниже должен объяснить почти все, что возможно в пространствах имен. За подробностями идем в официальную документацию.
__DIR__
— новая магическая константа
__DIR__ содержит директорию текущего файла – файла в котором она используется. Возвращает полный путь до текущего файла без закрывающего слэша, за исключением корневой директории.
__DIR__ можно заменить:
dirname(__FILE__)
$class::$foo
— динамичное указание класса
Это дает динамичный доступ к статическим методам/свойствам класса:
class C { static $foo = 'foo'; } $class = 'C'; echo $class::$foo; //> foo
const
— ключевое слово для создания констант вне классов
Сразу пример, где все понятно:
define('SHORTINIT', 'true'); // теперь можно объявить константу и так: const SHORTINIT = 'true';
В отличие define(), такие константы, должны быть объявлены в самой верхней области видимости, потому что они определяются при компилировании скрипта. Это значит, что их нельзя объявлять внутри функций/циклов/выражений if или try/ catch блоков.
static::method()
— статическое связывание
Статическое объявление метода/свойства связывает его с классом из которого оно вызывается, а не с тем в котором оно зарегистрировано. Посмотрим на примере:
class A { static function who() { echo __CLASS__; } static function test1() { self::who(); } static function test2() { static::who(); // статическое связывание } } class B extends A { static function who() { echo __CLASS__; } } echo B::test1(); //> A echo B::test2(); //> B echo B::who(); //> B
Подробнее про статическое связывание читайте в документации.
goto hell;
— оператор goto
Используется для перехода в другую часть программы. Место, куда необходимо перейти указывается с помощью метки, за которой ставится двоеточие, после оператора goto указывается желаемая метка для перехода.
Целевая метка должна находиться в том же файле, в том же контексте. Т.е. нельзя выйти за границы функции или метода, а значит нельзя перейти внутрь любой функции.
Также нельзя перейти внутрь любой циклической структуры или оператора switch. Но можно выйти из любой циклической структуры, поэтому «goto» удобен как замена многоуровневых break.
Пример использования goto:
function zayac(){ $i = 1; $out = ''; start: $out .= ($i > 1 ? '-' : '' ) .$i; if( $i++ < 5 ){ goto start; } return $out . ' вышел зайчик погулять'; } echo zayac(); //> 1-2-3-4-5 вышел зайчик погулять
Пример использования goto в цикле:
for( $i=0, $j=50; $i<100; $i++ ) { while( $j-- ) { if( $j==17 ) goto end; } } echo "i = $i"; // будет пропущено end: echo 'j дошло до 17';
__callStatic(), __invoke()
— магические методы
__callStatic()
— срабатывает, когда вызывается несуществующий метод из статического контекста: Foo::bar()
:
class A { static function __callStatic( $name, $args ){ return $name .' '. print_r( $args, 1 ); } } echo A::no_matter_what('bar'); /* Выведет: no_matter_what Array ( [0] => bar ) */
__invoke()
— срабатывает, когда объект выполняется как функция: $obj()
:
class A { function __invoke( $var ){ var_dump( $var ); } } $obj = new A; $obj('foo'); //> string(3) "foo"
PHP 5.4
- Wiki: PHP 5.4
- php.net: Возможности в версии PHP 5.4.
<?=
— короткая запись вывода на экран работает всегда
Wiki: https://wiki.php.net/rfc/shortags
Короткая запись о которой идет речь это: <?=
вместо <?php echo
.
Для работы такой короткой записи вывода на экран в версиях ниже 5.4 нужно было, чтобы опция short_open_tag
в php.ini была включена.
Пример длинной и короткой записи:
<a href="#"><?php echo $page ?></a> <a href="#"><?= $page ?></a>
[1,2]
— запись массива, без слова array
wiki: Short syntax for arrays
$a = [ 1, 2, 3, 4 ]; $a = [ 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4 ];
trait Class {}
— примеси (трейты)
Трейт – это аналог класса, который содержит в себе методы. Нужен он для «подмешивания» его в имеющийся класс, чтобы методы трейта стали методами класса в который он добавлен.
Несколько примесей можно задавать через запятую:
trait TR_A { public $var = 'var'; function foo() { return 'foo'; } } trait TR_B { function bar() { return 'bar'; } } class A { use TR_A, TR_B; // подмешиваем function hello() { return 'hello A'; } } $A = new A(); echo $A->foo(); // foo echo $A->bar(); // bar echo $A->hello(); // hello A echo $A->var; // var class B extends A { use TR_A, TR_B; function hello() { return 'hello B'; } } $B = new B(); echo $B->foo(); // foo echo $B->bar(); // bar echo $B->hello(); // hello B
Приоритеты трейтов
При совпадении названий свойств/методов приоритеты расставляются так: текущий класс имеет наивысший приоритет, затем трейт, а затем расширяемый класс. Другими словами: элементы из текущего класса переопределяют элементы в трейте, которые в свою очередь переопределяют унаследованные элементы.
Статический доступ к методу примеси из класса
Когда в класс подмешивается trait, то его методы становятся методами класса, включая статические и статический доступ:
trait A { static function func(){ echo 'A'; } } class B { use A; } B::func(); //> A
Подробнее про трейты читайте в документации
foo()[0]
— быстрое получение элемента массива
Теперь не нужно сохранять массив, который вернула функция/метод в переменную и получать элемент массива из этой переменной. Можно сразу получать элемент из функции/метода:
$foo = func()[0]; $foo = Class::func()[0];
(new Foo)->method()
— доступ к элементу объекта при его создании
$foo = (new Foo)->method(); $foo = (new Foo)->property; $foo = (new Foo)[0]; // было так $obj = new Foo; $foo = $obj->method();
Class::{'foo'}()
— динамичное указание метода
Чтобы вызвать статический метод/свойство класса, не нужно запоминать его в отдельную переменную:
class A { static function foo() { echo "Hello world!"; } } $x = "f"; A::{ $x .'oo' }();
callable
— новый тип для аргументов функции/метода
Авто-проверка передаваемых данных в функции/методы, известная как «контроль типа» (typehint), продолжает развиваться и теперь понимает слово callable
.
Раньше для автоматической проверки типа передаваемого параметра, в аргументах функции/метода можно было указывать только: array
или имя класса
.
Теперь, можно указать еще: callable
— значит, что передаваемый аргумент должен быть вызываемым, т.е. удовлетворяет условию is_callable( $arg, false ).
Пример:
function func( callable $callback ){ return true; } func('trim'); //> true func( function(){} ); //> true $db = new wpdb(); func( array($db, 'query') ); //> true func('my_trim'); //> fatal error: Argument 1 passed to func() must be callable, string given
@
— улучшена производительность
Оператор @
нужен для подавления вывода ошибок любого уровня. Вообще его использовать не рекомендуется, но иногда с ним короче:
if( @ $_GET['foo'] ) echo 'OK'; // или так if( isset($_GET['foo']) && $_GET['foo'] ) echo 'OK'; // раньше так работало быстрее раз в 20, теперь раз в 5
Использовать @
нужно как можно реже и очень осторожно, потому что часто заметки и предупреждения дают понять, что логика кода работает неправильно. Например, у меня бывало что лезу поправить казалось бы безобидный NOTICE, но при анализе выясняется что ошибка появилась из-за неправильной логики кода, которая изменилась в процессе расширения кода…
PHP 5.5
- Wiki: PHP 5.5
- php.net: Новые возможности в PHP 5.5
[1,3,4][2], "foobar"[2]
— разыменования созданных массивов/строк
echo array(1, 2, 3)[0]; // 1 echo [1,3,4][2]; // 4 echo "foobar"[0] // f // это может пригодиться для быстрой генерации: echo 'abcdefghijk'[ rand(0,10) ]; // получим одну из букв: 'abcdefghijk'
empty()
— можно применять к результатам функций и выражений
Раньше empty() мог принимать только переменные, теперь можно передавать сами выражения без необходимости сохранять результат в отдельную переменную:
empty( $object->get() );
list() в foreach
В foreach стало возможным использовать list():
$array = [ [1, 2], [3, 4], ]; foreach( $array as list($a, $b) ){ echo $a; echo $b; } // получим: 1234
finally
— в конструкции try/catch
Выбрасывать и ловить исключения можно с PHP 5. Такой подход позволяет контролировать выполнение кода, если есть подозрение, что в нем что-то может пойти не так.
А с версии 5.5. в эту конструкцию добавили третий блок finally
. Блок finally выполняется всегда после завершается конструкции try/catch. Он выполняется даже когда код try вызвал фатальную ошибку:
try { echo 'Тут что-то деламе... '; // выбрасываем throw new Exception('Лови меня! '); } // ловим catch( Exception $e ){ echo $e->getMessage(); // выведет: Лови меня! } finally { echo 'А это выводиться всегда!'; }
Нужен finally для удобства, и дополнительных возможностей. С ним можно будет писать меньше кода и можно, например, удобно чистить память, когда это нужно.
Пару домонстрационных примеров:
Меньше кода
Допустим, нам нужно выполнить функцию close() в любому случае, было выброшено исключение или нет:
try { my_function(); } catch( Exception $e ){ // close(); // эта строка нужна была бы без finally echo $e->getMessage(); // выведет: Лови меня! } finally { close(); } //close(); // эта строка нужна была бы без finally
Больше возможностей
Допустим мы открыли соединение с БД до выполнения кода и есть вероятность что код вызовет ошибку и открытое соединение не закроется, а нам нужно его закрыть в любом случае. finally как раз кстати:
$db = mysqli_connect(); try { my_function( $db ); // результат работы функции может вызвать фатальную ошибку... } // исключение можно не обрабатывать finally { mysqli_close($db); }
- Подробнее про finally читайте статью на хабре.
Class::class
— для получение имени класса в пространствах
Появилось ключевое слово class
для классов, которое выводит название класса. В обычном режиме нам это не нужно, а вот при работе с пространствами (namespace) — это удобно:
namespace test; class A {} echo A::class; //> testA
yield
— создание генераторов
Если говорить простым языком: yield
похожа на return, она также возвращает значение, но она не обрывает работу функции, а приостанавливает её до тех пор пока не будет запрошено следующее значение. Благодаря этому создавать генераторы стало удобнее.
Пример генератора:
function generator() { for( $i = 1; $i <= 3; $i++ ){ yield $i; // выброс значения } } foreach( generator() as $value ){ echo "$value "; } // выведет: '1 2 3 '
Как это работает на самом деле?
yield
возвращает специальный объект — Generator. Когда функция generator() вызывается в цикле, например foreach, PHP выполнит код функции до первой встречи слова yield, на котором PHP прервет работу функции, запомнит позицию и выбросит значение (объект Generator). Затем, foreach обработает значение и вызовет метод next() у полученного объекта Generator. PHP снова выполнит код функции generator(), только начнет его не с начала, а с прошлой позиции, и опять, до слова yield, которое опять выбросит объект Generator. Работа цикла прервется тогда, когда функция generator() дойдет до конца (не вернет yield), или если она будет прервана с помощью return;
.
Пример передачи генератора в переменную (повторный вызов в этом случае не работает):
function generator() { for( $i = 1; $i <= 3; $i++ ){ yield $i; // выброс значения } } $gen = generator(); foreach( $gen as $value ){ echo "$value "; } // выведет: '1 2 3 ' // Fatal error: Uncaught Exception: Cannot traverse an already closed generator foreach( $gen as $value ){ echo "$value "; }
Пример генератора который возвращает пару: ключ/значение:
function generator( $input ){ foreach( explode('.', $input) as $part ){ list( $num, $name ) = explode(' - ', $part ); yield $num => trim($name); } } $input = '1 - один. 2 - два. 3 - три'; foreach( generator( $input ) as $num => $name ){ echo "$num ($name) "; }
Кратко о генераторах
- Не добавляют нового функционала в язык
- Быстрее
- Возобновление работы генератора происходит с последнего «выброса» yield
- В генератор можно отправлять значения и исключения (через метод throw())
- Генераторы однонаправлены, т.е. нельзя вернуться назад
- Меньше кода в большинстве случаев, более простые для понимания конструкции
- Чтобы лучше понять генераторы прочитайте эту статью на Хабре.
- Подробнее о генераторах в документации (рус.)
- Подробно о генераторах в документации (англ.)
API для хэширования паролей
habrahabr.ru: API хэширования паролей в PHP 5.5.
Теперь PHP из коробки предлагает правильный способ хэшировать пароли. Новый API хэширования паролей предоставляет четыре функции:
password_hash()
— используется для хэширования пароля. В WP для этого есть своя функция wp_hash_password().$hash = password_hash( $passwod, PASSWORD_DEFAULT );
password_verify()
— используется для проверки пароля на соответствие хэшу. В WP для этого есть своя функция wp_check_password().if( password_verify( $password, $hash ) ){ // Success! }
password_needs_rehash()
— используется для проверки необходимости создать новый хэш.password_get_info()
— возвращает имя алгоритма хеширования и различные параметры, используемые при хэшировании.
PHP 5.6
- Wiki: PHP 5.6
- php.net: Новые возможности PHP 5.6.
- habrahabr.ru: Остальные новинки PHP 5.6 не связанные с синтаксисом.
const PLUS = 1 + 2;
— скалярные выражения в константах/свойствах/аргументах функции
Теперь стало возможным указывать в значения констант примитивные PHP выражения (выражения из скаляров).
Точнее, новинка касается не только констант, а всего где раньше PHP ожидал статическое значение. Теперь вместо статики можно указать выражение из чисел/строк/констант. Если точнее, то PHP выражение можно указывать: в константах/свойствах класса и в значении аргумента функции по умолчанию.
const ONE = 1; const TWO = ONE * 2; class C { const THREE = TWO + 1; const ONE_THIRD = ONE / self::THREE; const SENTENCE = 'Значение THREE равно '. self::THREE; public function f( $a = ONE + self::THREE ){ return $a; } } echo (new C)->f() .' - '. C::SENTENCE; //> 4 - Значение THREE равно 3
const ARR = ['a', 'b'];
— константа может хранить массив
Стало возможным держать в константе массивы:
const ARR = ['a', 'b']; echo ARR[0]; //> a
func( ...$args )
или func( ...[2, 3] )
— распаковка аргументов
Wiki: Argument Unpacking
Оператор ...
еще называют «Splat Оператор», «Scatter operator» или «Spread operator».
Когда мы не знали заранее, сколько параметров может получить функция, нам приходилось внутри функции обрабатывать переданные параметры с помощью специальных функций: func_num_args(), func_get_arg(), func_get_args().
Теперь они не нужны и мы можем получить все параметры в одной переменной, для этого перед этой переменной нужно указать оператор ...
:
function sum( ...$numbers ){ $plus = 0; foreach( $numbers as $n ){ $plus += $n; } return $plus; } echo sum( 1, 2, 3 ); //> 6
Еще пример:
function func( ...$numbers ){ return print_r( $numbers, 1 ); } echo func(1, 2, 3); /* Получим: Array ( [0] => 1 [1] => 2 [2] => 3 ) */
Быстрая распаковка передаваемых параметров функции
Теперь с помощью splat оператора ...
, можно указать параметры функции сразу из значений массива:
function plus( $a, $b, $c ){ return $a + $b + $c; } $array = [2, 3]; echo plus( 1, ...$array ); //> 6 // или так echo plus( 1, ...[2, 3] ); //> 6
Замена функции call_user_func_array()
Теперь call_user_func_array( $callback, $param_arr )
, которая обычно не самая быстрая, можно заменить так:
$params = [ 1, 2, 3 ]; $callback( ...$params );
Распаковка прямо в массив
Ассоциативные массивы распаковывать нельзя.
// $arr1 = [ 'key' => 1 ] вызовет ошибку Fatal error $arr1 = [ 'foo', 100 ]; $arr2 = [ 'val', 200 ]; $arr = [ 1,89, 'str', ...$arr1, 22 ,...$arr2, 456, 52 ]; print_r( $arr ); /* Array ( [0] => 1 [1] => 89 [2] => str [3] => foo [4] => 100 [5] => 22 [6] => val [7] => 200 [8] => 456 [9] => 52 ) */
**
— оператор возведения в степень
До php 5.6, чтобы возвести число в степень нужно было использовать функцию pow(2,2);
, а теперь есть оператор **
:
// пример 1 echo $a = 2 ** 2; //> 4 // пример 2 $a = 2; echo $a **= 2; //> 4 // пример 3 echo $a = 2 ** 3 ** 2; //> 512 = 2^9
use function
и use const
— импорт функций и констант в пространство
Теперь стало возможным при помощью ключевого слова use
подключать функции или константы другого пространства в наше:
namespace ourspace { const FOO = 42; function func() { echo __FUNCTION__; } } namespace myspace { use const ourspaceFOO; use function ourspacefunc; echo FOO .' - '. func(); //> 42 - ourspacefunc }
Куда делся PHP 6?
Умер не родившись… В ядро PHP 6 планировали внедрить полную поддержку юникода, но затея оказалась слишком амбициозной, а объем работ слишком велик. К тому моменту, когда это стало понятно, о PHP 6 уже было написано не мало статей. Чтобы не было путаницы, из-за того что новая версия стала преследовать совсем другие цели (производительность) и сильно отличалась по концепции от PHP 6, было решено пропустить PHP 6. Еще одной причиной стало наличие весомого количества недоделанного кода в репозитории PHP, который решили не трогать, чтобы тот в ответ тоже никого не трогал…
PHP 7
- Wiki: PHP 7.0
- php.net: Что нового в PHP 7
- skillz.ru: Новинки PHP 7 – часть 1 и часть 2
3 декабря 2015 года было объявлено о выходе PHP 7. Новая версия основывается на экспериментальной ветке PHP, которая изначально называлась phpng (PHPNextGeneration – следующее поколение), и разрабатывалась с упором на увеличение производительности и уменьшение потребления памяти.
Самой важной новинкой стало изменение ядра интерпретатора: теперь он называется PHPNG (Next Generation). Благодаря PHPNG удалось увеличить скорость обработки скриптов почти в двое по сравнению с PHP 5.x. Так же появился более эффективный менеджер памяти.
Прирост в скорости на практике хорошо виден на этой картинке. А для WordPress прирост в скорости выглядит так:
Подробнее смотрите в тестах PHP 7
Синтаксические новинки PHP 7:
$a ?? ''
— isset и получение значения
Новый оператор слияния с NULL (NULL coalescing operator) ??
— это сокращение проверки isset и получения значения, если проверка пройдена.
Такая проверка часто была нужна в тернарном операторе ?:
:
// Получит значение $_GET['foo'], если переменная установлена или не пустая, иначе получит 'default' $foo = $_GET['foo'] ?? 'default'; // Запись равносильна этой $foo = isset($_GET['foo']) ? $_GET['foo'] : 'default'; // или этой $foo = @ $_GET['foo'] ?: 'default'; // удобная проверка при получении $_GET параметра if( $_GET['foo'] ?? 0 ){ } // раньше писали так if( isset($_GET['foo']) && $_GET['foo'] ){ }
Так же, проверять можно по цепочке:
$foo = $_GET['foo'] ?? $_POST['foo'] ?? 'default'; // вернет: $_GET['foo'], если его нет, то $_POST['foo'], если нет, то 'default'
$a <=> $b
— три сравнения сразу: больше, равно, меньше
Новый оператор сравнения <=>
— «spaceship operator» (космический корабль). Сравнивает 2 переменные и возвращает результат сравнения в виде числа:
-1
— если в сравнении подходит первый символ оператора<
0
— подходит второй символ=
1
— подходит третий символ>
// Числа echo 1 <=> 1; // 0 echo 1 <=> 2; // -1 echo 2 <=> 1; // 1 // Дробные числа echo 1.5 <=> 1.5; // 0 echo 1.5 <=> 2.5; // -1 echo 2.5 <=> 1.5; // 1 // Строки echo "a" <=> "a"; // 0 echo "a" <=> "b"; // -1 echo "b" <=> "a"; // 1
Оператор | Эквивалент <=> |
---|---|
$a < $b | ($a <=> $b) === -1 |
$a <= $b | ($a <=> $b) === -1 || ($a <=> $b) === 0 |
$a == $b | ($a <=> $b) === 0 |
$a != $b | ($a <=> $b) !== 0 |
$a >= $b | ($a <=> $b) === 1 || ($a <=> $b) === 0 |
$a > $b | ($a <=> $b) === 1 |
Удобен для использования в usort():
usort( $products, function( $product1, $product2 ){ return $product1->price() <=> $product2->price(); } );
Можно использовать такой хак, чтобы не писать вложенных тернарных операторов:
$count = 1; // это $class = ( $count === 0 ) ? 'null' : ( $count > 0 ? 'plus' : 'minus' ); // plus // можно записать так $class = [ 'minus', 'null', 'plus' ][ ( $count <=> 0 ) + 1 ]; // plus
define( 'FOO', [1,2] );
— массив в define константе
Константы могут содержать массивы еще с PHP 5.6. Но тогда их можно было передавать только через ключевое слово const. Теперь их можно указывать еще и через define()
.
define('ANIMALS', ['dog', 'cat', 'bird']); echo ANIMALS[2]; //> bird
use namespace{A, B, C as c};
— группировка импорта
Теперь для краткой записи, импорт данных в наше пространство можно группировать:
// PHP 7 use somenamespace{ ClassA, ClassB, ClassC as C }; use function somenamespace{ fn_a, fn_b, fn_c }; use const somenamespace{ СonstA, ConstB, ConstC }; // тоже самое до PHP 7 use somenamespaceClassA; use somenamespaceClassB; use somenamespaceClassC as C; use function somenamespacefn_a; use function somenamespacefn_b; use function somenamespacefn_c; use const somenamespaceConstA; use const somenamespaceConstB; use const somenamespaceConstC;
int, float, bool
— новые типы параметров функции/метода
Авто-проверка типа передаваемых данных в функции/методы, известная как «контроль типа» (typehint), продолжает развиваться и теперь понимает скаляры: int
, float
, bool
, string
. Раньше понимались только типы: array
, имя класса
или callable
(с версии 5.4).
Пример:
function foo( int $a, bool $b, callable $с, array $d, WP_Post $e ) { return var_dump( $a, $b, $c, $d, $e ); } foo( 1, true, 'trim', array(1), get_post(1) ); /* выведет: int(1) bool(true) NULL array(1) { [0]=> int(1) } object(WP_Post)#2660 (24) { ...данные объекта... } */ // если указать неверный тип: foo( 'foo', true, 'trim', array(1), get_post(1) ); // Получим ошибку Fatal error: Argument 1 passed to A::foo() must be of the type integer, string given
Режим строгой типизации
Если указан тип int
и передать строку '123'
то проверка все равно будет пройдена, и php превратить строку в число.
function func( int $num ){ var_dump( $num ); } func('123'); //> int(123)
Но что, если нужно получать именно число 123? Для этого можно включить режим строгой типизации, поместив в самое начало файла такую строку:
declare(strict_types=1);
Это объявление должно быть первой строкой в файле, до выполнения какого-либо кода. Оно затрагивает только код файла и только вызовы и возвращаемые значения в этом файле.
Заметка: если строгая типизация указана в файле X, но не указана в файле Y и в файле Y вызывается функция из файла X. То вызов такой функции не будет подвержен строгой типизации!
Читайте по типизации статью на Хабре и вот еще интересная статья.
int, float, bool, array
— типы возврата функции/метода
Указывать принимаемый тип, можно еще с версии PHP 5.3. А вот указать какой тип функция/метод должна вернуть доступно только с версии PHP 7. Тут понимаются все типы: string
, int
, float
, bool
, array
, callable
, self
(в методах), parent
(в методах) , Closure
, имя класса
, имя интерфейса
.
Синтаксис:
function func( $var ): int{ /* код функции */ } function func( $var ): string{ } function func( $var ): float{ } function func( $var ): bool{ } function func( $var ): array{ } function func( $var ): callable{ } function func( $var ): Closure{ } function func( $var ): WP_Post{ } // может вернуть только объект класса WP_Post class A extends B { function func( $var ): self{ } function func( $var ): parent{ } }
Рабочие примеры:
// Пример 1: function func( $var ): int { return $var; } echo func( 123 ); //> 123 echo func( 'asfd' ); //> вызовет ошибку: Fatal error: Uncaught TypeError: Return value of func() must be of the type integer, string returned // Пример 2: Closure function func(): Closure { return function( $var ){ return $var .' + 2 = 3'; }; } echo func()( 1 ); //> 1 + 2 = 3
Возвращаемые типы при наследовании методов класса
При наследовании в классах, дочерние методы должны иметь такие же возвращаемые типы как и в родительском классе/интерфейсе:
class A { function func() : int { return 123; } } class B extends A { function func() : string { return '123'; } // такое объявление функции вызовет ошибку: // Fatal error: Declaration of B::func(): string must be compatible with A::func(): int // т.е. тип int должен совпадать! }
Навороченный пример того, как можно писать в PHP 7
Тут сразу несколько новинок:
- принимаемый и возвращаемый тип;
- объединение и распаковка параметров с помощью
...
; - пример создания анонимной функции с указанием возвращаемого типа данных.
function arraysSum( array ...$arrays ): array { return array_map( function( array $array ): int { return array_sum( $array ); }, $arrays ); } print_r( arraysSum( [1,2,3], [4,5,6], [7,8,9] ) ); /* Выведет: Array ( [0] => 6 [1] => 15 [2] => 24 ) */
foo()(), $a::$b::$c, $$foo->bar
— единый синтаксис: СЛЕВА НАПРАВО
Важная новинка! Теперь обращения к сложносочиненным переменным разбираются последовательно СЛЕВА НАПРАВО.
Примеры новых возможностей:
// можно не указывать комбинирующие скобки $foo()['bar']() [ $obj1, $obj2 ][0]->prop getStr()[0] // поддерживает вложенность :: $foo['bar']::$baz // > ( $foo['bar'] )::$baz $foo::$bar::$baz // > ( $foo::$bar )::$baz $foo->bar()::baz() // > ( $foo->bar() )::$baz // поддерживает вложенные () foo()() // вызывает результат foo() → ( foo() )() $foo->bar()() // > ( $foo->bar() )() Foo::bar()() // > ( Foo::bar() )() $foo()() // > ( $foo() )() // Операторы над выражениями заключенными в () ( function() { ... } )() // IIFE синтаксис JS ( $obj->closure )() // и т.д. (...)['foo'] (...)->foo (...)->foo() (...)::$foo (...)::foo() (...)() // все операции по разименованию скаляров "string"->toLower() [ $obj, 'method' ]() 'Foo'::$bar
Примеры разницы старого и нового распознавания:
// строка // старое понимание // новое понимание $foo['bar']['baz'] ${ $foo['bar']['baz'] } ( $foo )['bar']['baz'] $foo->$bar['baz'] $foo->{ $bar['baz'] } ( $foo->$bar )['baz'] $foo->$bar['baz']() $foo->{ $bar['baz'] }() ( $foo->$bar )['baz']() Foo::$bar['baz']() Foo::{ $bar['baz'] }() ( Foo::$bar )['baz']()
Старый код написанный с использованием {}
для обработки переменных возможно не будет работать в новой версии PHP 7.
foreach — изменена логика работы
Теперь foreach не переключает автоматически внутренний указатель перебираемого массива, т.е. next() не работает автоматически.
Так же, если значение массива передается в foreach по ссылке, то он всегда работает с оригиналом массива. Если значение не передается по ссылке, то foreach всегда работает с копией массива и оригинальный массив не затрагивается при изменении его внутри foreach.
Переключение указателей и влияние на работу цикла в PHP 7:
// Пример 1: автоматически не переключает внутренний переключатель next() $a = [1,2,3]; foreach($a as $v) { echo "$v-". current($a) .' '; } // 1-1 2-1 3-1 (php5: 1-2 2-2 3-2) $a = [1,2,3]; $b = &$a; foreach($b as $v) { echo "$v-". current($b) .' '; } // 1-1 2-1 3-1 (php5: 1-2 2-3 3-) $a = [1,2,3]; $b = $a; foreach($b as $v) { echo "$v-". current($b) .' '; } // 1-1 2-1 3-1 (php5: 1-1 2-1 3-1) $a = [1,2,3]; foreach($a as & $v) { echo "$v-". current($a) .' '; } // 1-1 2-1 3-1 (php5: 1-2 2-3 3-) // В PHP 7 все строки выведут один результат: 1-1 2-1 3-1 // В PHP 5 он будет разный // Пример 2: внутренний переключатель можно переключить с помощью next() $a = [1,2,3]; foreach($a as $v) { echo "$v-". current($a) .' '; next($a); } // 1-1 2-2 3-3 (php5: 1-2 2-3 3-) $a = [1,2,3]; $b = &$a; foreach($b as $v) { echo "$v-". current($b) .' '; next($b); } // 1-1 2-2 3-3 (php5: 1-2 2-3 3-) $a = [1,2,3]; $b = $a; foreach($b as $v) { echo "$v-". current($b) .' '; next($b); } // 1-1 2-2 3-3 (php5: 1-2 2-3 3-) $a = [1,2,3]; foreach($a as & $v) { echo "$v-". current($a) .' '; next($a); } // 1-1 2-2 3-3 (php5: 1-2 2-3 3-) // В PHP 7 все строки выведут один результат: 1-1 2-2 3-3 // В PHP 5 результат тоже будет один, но другой: 1-2 2-3 3- // Пример 3: // при &$v foreach работает с оригиналом $a и изменение массива влияет на цикл $a = [1,2,3]; foreach($a as &$v) { echo "$v "; unset($a[1]); } // 1 3 (php5: 1 3) $a = [1,2,3]; $b = &$a; foreach($b as &$v) { echo "$v "; unset($a[1]); } // 1 3 (php5: 1 3) // в PHP 5 и PHP 7 совпадают // при $v foreach работает с копией $a и изменение массива НЕ влияет на цикл $a = [1,2,3]; foreach($a as $v) { echo "$v "; unset($a[1]); } // 1 2 3 (php5: 1 2 3) $a = [1,2,3]; $b = &$a; foreach($b as $v) { echo "$v "; unset($b[1]); } // 1 2 3 (php5: 1 3) // в PHP 5 и PHP 7 результаты отличаются
$class = new class{}
— анонимные классы
Wiki: Anonymous Classes
Анонимные классы позволяют делать тоже самое что и обычные классы: передавать данные в конструктор, наследовать другие классы, использовать трейты и т.п.
$class = new class { public function echo( $msg ){ echo $msg; } }; $class->echo('Привет!'); // выведет на экран: "Привет!"
Расширение классов работает как и ожидается:
class Foo {} $child = new class extends Foo {}; var_dump( $child instanceof Foo ); //> true
Использование треитов:
trait Foo { public function method() { return "bar"; } } $class = new class { use Foo; }; var_dump( $class->method() ); //> string(3) "bar"
Подробнее про анонимные классы читайте в документации.
yield ... return 99;
— возврат выражений в генераторах
Wiki: Generator Return Expressions
Функции-генераторы появились в PHP 5.5. Но там можно было использовать return, только чтобы прервать работу генератора. Теперь return может возвращать выражение (значение/массив/другой генератор), а не только NULL. Но сделать это можно только в конце работы генератора.
Получить возвращенное значение можно методом getReturn()
, но только по завершении работы генератора.
Возможность явно вернуть последнее значение упрощает работу с генераторами:
теперь не нужно проверять является ли значение последним, а просто вызываем getReturn().
function gen() { yield 1; yield 2; return 3; } $gen = gen(); // если генератор еще ничего не вернул, то вызов такой строки // echo $gen->getReturn(); // вызовет ошибку: Fatal error: Uncaught Exception: Cannot get return value of a generator that hasn't returned foreach( $gen as $val ) { echo $val; } echo $gen->getReturn(); // результат работы этого кода выведет на экран: 123
yield from gen()
— делегирование генераторов
Wiki: Generator Delegation
Позволяет разбить сложный генератор на несколько простых.
Для этого используется новый синтаксис: yield from <expr>
, где <expr> может быть значением (скаляром), массивом или другим генератором.
<expr> будет работать до тех пор, пока возвращает данные, затем выполнение продолжится в генераторе откуда <expr> был вызван. Смотрите пример:
function gen() { yield 1; yield from gen2(); yield 4; } function gen2(){ yield 2; yield 3; } $gen = gen(); foreach ( $gen as $val ) { echo $val; } // результат работы этого кода: 1234
Пример с массивом:
function g() { yield 1; yield from [2, 3, 4]; yield 5; } $g = g(); foreach ( $g as $yielded ) { echo $yielded; } // выведет: 12345
Пример с return из дочернего генератора:
function gen() { yield 1; $sub_gen = yield from sub_gen(); yield 4; return $sub_gen; } function sub_gen() { yield 2; yield 3; return 42; } $gen = gen(); foreach( $gen as $val ) { echo $val; } echo ' - '. $gen->getReturn(); // выведет: 1234 - 42
Остальные новинки PHP 7.0
- Синтаксис конструкторов в стиле PHP 4 (имя метода конструктора совпадает с именем класса) теперь считается устаревшим.
- Статичные вызовы
::
нестатичных методов теперь считаются устаревшими. list()
— изменение поведения. В PHP 5, list() устанавливал значения начиная с правого крайнего значения указанного массива, в PHP 7 параметры устанавливаются начиная с левого крайнего значения массива. Так же в PHP 5 list() умела разбивать строки на символы, в PHP 7 не работает со строками вообще…// Пример 1: обратное чтение // Если используются обычные переменные, то разницы нет list( $a, $b, $c ) = ['apple', 'bannana', 'cherry', 'damson']; var_dump( $a, $b, $c ); // php5 и php7 вернут: apple bannana cherry // А вот если устанавливаются элементы массива, то порядок будет отличаться $arr = []; list( $arr['a'], $arr['b'], $arr['c'] ) = ['apple', 'bannana', 'cherry', 'damson']; print_r( $arr ); /* PHP 7 Array ( [a] => apple [b] => bannana [c] => cherry ) PHP 5 Array ( [c] => cherry [b] => bannana [a] => apple ) */ // Пример 2: разбивание строк $str = 'ab'; list( $a, $b ) = $str; var_dump( $a, $b ); // В PHP 7: NULL NULL // В PHP 5: string(1) "a" string(1) "b"
- Поддержка юникод управляющих (escape-) последовательностей. Т.е. в строках
""
и heredoc можно использовать конструкциюuXXXX
для создания юникод символа. Вот так:echo "u{1F602}"; //> ?
- Класс IntlChar. Cодержит методы и константы для работы с юникодом.
printf('%x', IntlChar::CODEPOINT_MAX); // 10ffff echo IntlChar::ord('@'); //> 64 echo IntlChar::chr( 64 ); //> @ echo "u{1F602}"; //> ? echo IntlChar::ord("u{1F602}"); //> 128514 echo IntlChar::chr( 128514 ); //> ?
- Функция
intdiv()
— делит 2 числа и возвращает только целую часть от деления:echo intdiv(10, 3); //> 3 echo intdiv(5, 2); //> 2
session_start()
умеет получать параметры (стандартные настройки сессий из php.ini):session_start(['cache_limiter' => 'private']);
- Функция
preg_replace_callback_array()
— альтернатива preg_replace_callback(). Позволяет передать в качестве обратной функции – массив['/regex'/ => callback, ...]
:$str = 'a1a2a3'; $array = [ '~[0-9]~' => function ( $m ){ return $m[0] * 2; }, '~a~' => function ( $m ){ return $m[0] . '-'; } ]; echo preg_replace_callback_array( $array, $str ); //> a-2a-4a-6
- Можно использовать глобальные ключевые слова в названиях методов. Т.е. раньше нельзя было назвать метод словами: with/new/for/foreach/… — это приводило к ошибке. Теперь можно:
Class::new('Project Name'); $class->for('purpose here');
PHP 7.1
- Wiki: PHP 7.1
- github.com: Список изменений PHP 7.1
- php.net: Новые возможности PHP 7.1
- skillz.ru: Список изменений PHP 7.1
?string
— Обнуляемый тип (и null тип)
Типы для параметров и возвращаемых значений могут быть помечены как обнуляемые путем добавления префикса в виде знака вопроса. Это означает, что указанные параметры и возвращаемые значения, могут быть как указанного типа, так и NULL.
function testReturn(): ?string { return 'elePHPant'; } var_dump( testReturn() ); // string(10) "elePHPant" function testReturn(): ?string { return null; } var_dump( testReturn() ); // NULL function test( ?string $name ) { var_dump( $name ); } test('elePHPant'); // string(10) "elePHPant" test(null); // NULL test(); // Uncaught Error: Too few arguments to function test(), 0 passed in...
void
— возвращаемый тип
Теперь функции и методы, которые не должны ничего возвращать, можно помечать возвращаемым типом void. Оператор return при этом должен отсутствовать или должен быть пустым – return;
. Вызов return null;
вызовет ошибку.
function someMethod(): void { // работает если return отсутствует // работает с return; // не работает если return null; // не работает если return 123; }
['key'=>$var] = ['key'=>'Значение']
— Деструктурирование массивов
Wiki: Destructuring assignment from an array to variables (short list syntax).
Можно использовать синтаксис [] = []
для деструктуризации массивов и присвоения значений массива переменным – альтернатива функции list().
Пример с индексным массивом:
list( $one, $two ) = [ 'один', 'два' ]; // list style [ $one, $two ] = [ 'один', 'два' ]; // [] style echo "$one, $two"; //> один, два
Тоже самое можно сделать и с ассоциативным массивом, извлекая из них значения по ключам. Имена получаемых могут быть любыми, главное — совпадение по ключам.
$person = [ 'first' => 'Rasmus', 'last' => 'Lerdorf', 'manager' => true ]; // Порядок извлечения не важен [ 'last' => $lastname, 'first' => $firstname ] = $person; echo "$lastname, $firstname"; //> Lerdorf, Rasmus
Вложенная деструктуризация. Также можно присвоить значения вложенных массивов:
[ [$a, $b], [$c, $d] ] = [ [1, 2], [3, 4] ];
$options = [ 'enabled' => true, 'compression' => ['algo' => 'gzip'] ]; [ 'enabled' => $enabled, 'compression' => [ 'algo' => $algo, ] ] = $options;
Деструктуризация в foreach.
Синтаксис list() разрешен не только в левой части операции присваивания, но и в качестве переменной в цикле foreach. Новый синтаксис []
так же работает в этом случае:
$persons = [ [ 'first' => 'Rasmus', 'last' => 'Lerdorf' ], [ 'first' => 'Egor', 'last' => 'Drujo' ], [ 'first' => 'Telia', 'last' => 'Masterok' ], ]; foreach( $persons as [ 'first' => $first, 'last' => $last ] ){ echo "$first, $last"; }
list( 'id'=>$id ) = $data
— поддержка ключей в list()
Теперь оператор list() поддерживает ключи. Это позволяет деструктурировать массивы с нечисловыми или непоследовательными ключами.
$data = [ ["id" => 1, "name" => 'Tom'], ["id" => 2, "name" => 'Fred'], ]; // стиль list() list("id" => $id1, "name" => $name1) = $data[0]; // стиль [] [ "id" => $id1, "name" => $name1 ] = $data[0]; // стиль list() foreach ( $data as list("id" => $id, "name" => $name) ) { // logic here with $id and $name } // стиль [] foreach ( $data as ["id" => $id, "name" => $name] ) { // logic here with $id and $name }
Closure::fromCallable()
— новый статический метод Closure
В класс Closure добавлен новый статический метод для возможности легко преобразовать callable в объекты типа Closure.
class Test { public function exposeFunction(){ return Closure::fromCallable( [$this, 'privateFunction'] ); } private function privateFunction( $param ){ var_dump( $param ); } } $privFunc = (new Test)->exposeFunction(); $privFunc('значение'); //> string(16) "значение"
private const
— Область видимости констант в классах
Конец публичным константам, теперь для констант можно указать видимость:
class ConstClass { const CONST_ONE = 1; // public public const CONST_TWO = 2; protected const CONST_THREE = 3; private const CONST_FOUR = 4; }
iterable
— новый псевдо-тип
Wiki: RFC: Iterable
Введен новый тип iterable для передаваемых/возвращаемых значений. Может использоваться при передаче массивов или объектов, которые соответствуют интерфейсу Traversable.
function myfunc( iterable $data ){ foreach( $data as $k => $v ){ echo $k, ':', $v, PHP_EOL; } } // массив myfunc([10, 20, 30]); // 0:10 1:20 2:30 // объект myfunc( new SplFixedArray(3) ) // 0: 1: 2: // генератор function myGen(){ yield 10; yield 20; yield 30; } myfunc( myGen() ); // 0:10 1:20 2:30
?int = null
— тип передаваемых/возвращаемых значений
Wiki: Nullable Types
В PHP 7.0 стало возможным указать тип возвращаемых/передаваемых значений, но типизация не допускала использование null в качестве значения параметра.
В PHP 7.1 для разрешения null перед типом параметра указывается ?
:
function myfunc( ?int $i ) : ?int { var_dump($a); return $a; } myfunc( 20 ); // int(20) myfunc( null ); // null myfunc(); // Ошибка: Uncaught Error: Too few arguments to function name(), 0 passed
$string[-1]
— отрицательное значение смещения в строках
Добавлена возможность использовать отрицательное значение для смещения в строках
echo $string[-1]; // последний символ
catch (First | Second $e)
— обработка нескольких исключений в одном блоке catch
В блоке catch теперь можно обрабатывать несколько исключений, перечисляя их через символ вертикальной черты (|). Это может быть полезно, если различные исключения обрабатываются одинаково.
try { // Какой то код } catch ( FirstException | SecondException $e ) { // Обрабатываем оба исключения }
Заметки по PHP 7.1
PHP движется в сторону строгой типизации данных и при переходе на 7.1 я столкнулся с ФАТАЛЬНОЙ ошибкой. И мне это показалось очень странным. Приведу пример:
$foo = ''; $foo['bar'] = 'мир'; // Warning: Illegal string offset 'bar' $foo['bar'][] = 'мир'; // Fatal error: Uncaught Error: Cannot use string offset as an array // фатальная ошибка: нельзя использовать отступ строки как массив...
При Warning PHP еще работает, а дальше уже нет! А еще в 7.0 код просто работал, даже без предупреждений и нотисов… Похоже на недоработку в PHP 7.1.
PHP 7.2
- Wiki: PHP 7.2
- github: Список изменений PHP 7.2
Закрывающая запятая для любых списков
wiki: https://wiki.php.net/rfc/list-syntax-trailing-commas
В следующих списках допускается использование запятых в конце:
- Сгруппированные пространства имен.
- Аргументы функций/методов (объявления и вызовы).
- Реализации интерфейсов в классе.
- Реализации признаков в классе.
- Списки членов класса.
- Наследование переменных из родительской области видимости в анонимных функциях.
// Arrays (already possible) $array = [1, 2, 3,]; // Grouped namepaces use FooBar{ Foo, Bar, Baz, }; // Function/method arguments (call) fooCall($arg1, $arg2, $arg3,); class Foo implements // Interface implementations on a class FooInterface, BarInterface, BazInterface, { // Trait implementations on a class use FooTrait, BarTrait, BazTrait, ; // Class member lists const A = 1010, B = 1021, C = 1032, D = 1043, ; protected $a = 'foo', $b = 'bar', $c = 'baz', ; private $blah, ; // Function/method arguments (declaration) function something(FooBarBazInterface $in, FooBarBazInterface $out,) : bool { } } // Inheriting variables from the parent scope in anonymous functions $foo = function ($bar) use ( $a, $b, $c, ) { // . . . };
PHP 7.3
- Wiki: PHP 7.3
- github: Список изменений PHP 7.3
Улучшен синтаксис Heredoc и Nowdoc
wiki: https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes
Закрывающий маркер теперь может иметь отступы TAB или Пробелы.
Синтаксис heredoc
и nowdoc
имели очень жесткие требования. Это привело к тому, что разработчики начали их избегать, поскольку их использование в коде
выглядело некрасиво и ухудшало читабельность.
Поэтому были внесены два изменения:
Разрешить отступ для закрывающего маркера.
// новый синтаксис: class foo { public $bar = <<<EOT bar EOT; } // раньше приходилось писать так: <?php class foo { public $bar = <<<EOT bar EOT; }
Еще примеры как это работает с отступами:
// no indentation echo <<<END b c END; /* b c */ // 4 spaces of indentation echo <<<END b c END; /* b c */
Убрать требование новой строки после закрывающего маркера.
Стало возможно писать так:
stringManipulator(<<<END b c END); $values = [<<<END b c END, 'd e f'];
Было так:
stringManipulator(<<<END a b c END ); $values = [<<<END a b c END , 'd e f'];
list( &$a )
— cсылки в list()
В PHP уже давно существует list() и ссылки. Однако до php 7.3 не было возможности использовать присваивание ссылок с помощью list().
$array = [ 1, 2 ]; list( $a, &$b ) = $array; // или через деструктуризацию [ $a, &$b ] = $array;
Конечно, это работает так же, как обычно работает list(), поэтому вы можете использовать его с вложенным list() и пропускать значения:
$array = [ 1, 2, 3, [3, 4] ]; list( &$a, $b,, list( &$c, $d ) ) = $array;
Он также работает с функцией foreach():
$array = [ [1, 2], [3, 4] ]; foreach ( $array as list( &$a, $b ) ) { $a = 7; } var_dump( $array ) /* array(2) { [0]=> array(2) { [0]=> int(7) [1]=> int(2) } [1]=> array(2) { [0]=> &int(7) [1]=> int(4) } } */
is_countable()
— функция
Этот RFC предлагает новую функцию типа, которая возвращает true, если заданное значение является типом массива или экземпляром интерфейса Countable.
Нужно было проверять так:
if ( is_array($foo) || $foo instanceof Countable ) { // $foo is countable }
Теперь можно так:
if ( is_countable($foo) ) { // $foo is countable }
Еще примеры:
var_dump(is_countable([1, 2, 3])); // bool(true) var_dump(is_countable(new ArrayIterator(['foo', 'bar', 'baz']))); // bool(true) var_dump(is_countable(new ArrayIterator())); // bool(true) var_dump(is_countable(new stdClass())); // bool(false)
$foo = [ '', [] ]; if ( is_countable( $foo ) ) { var_dump( count( $foo ) ); // int(2) }
array_(key|value)_(first|last)()
— новые функции
Поскольку массивы являются мощной структурой данных, в некоторых случаях удобно получить первый или последний ключ/значение массива без обходного пути. Для выполнения этой задачи данный RFC добавляет в ядро четыре функции:
$key = array_key_first( $array ); $key = array_key_last( $array ); $value = array_value_first( $array ); $value = array_value_last( $array );
PHP 7.4
- Wiki: PHP 7.4
- github: Список изменений PHP 7.4
[ ...$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()
- Оператор Spread должен иметь лучшую производительность, чем array_merge(). Потому что он является структурой языка, а array_merge() – вызовом функции, а также для постоянных массивов может быть выполнена оптимизация времени компиляции.
- 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 8.0
- Wiki: PHP 8.0
- github: Список изменений PHP 8.0
PHP 8.1
- Wiki: PHP 8.1
- github: Список изменений PHP 8.1
PHP 8.2
- Wiki: PHP 8.2
- github: Список изменений PHP 8.2