Что нового в 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 и получение значения
Wiki: Null Coalesce Operator
Новый оператор слияния с 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
— три сравнения сразу: больше, равно, меньше
Wiki: Combined Comparison (Spaceship) Operator
Новый оператор сравнения <=>
— «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 name\space\{A, B, C as c};
— группировка импорта
Wiki: Group Use Declarations
Теперь для краткой записи, импорт данных в наше пространство можно группировать:
// PHP 7 use some\namespace\{ ClassA, ClassB, ClassC as C }; use function some\namespace\{ fn_a, fn_b, fn_c }; use const some\namespace\{ СonstA, ConstB, ConstC }; // тоже самое до PHP 7 use some\namespace\ClassA; use some\namespace\ClassB; use some\namespace\ClassC as C; use function some\namespace\fn_a; use function some\namespace\fn_b; use function some\namespace\fn_c; use const some\namespace\ConstA; use const some\namespace\ConstB; use const some\namespace\ConstC;
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
— изменена логика работы
wiki: Fix "foreach" behavior
Теперь foreach не переключает автоматически внутренний указатель перебираемого массива, т.е. next() не работает автоматически:
$arr = [ 1, 2, 3, 4 ]; foreach( $arr as $val ){ echo key( $arr ) . ' '; } // PHP 5.6: 1 1 1 1 // PHP 7.0: 0 0 0 0 $arr = [ 1, 2, 3, 4 ]; foreach( $arr as & $val ){ echo key( $arr ) . ' '; } // PHP 5.6: 1 2 3 // PHP 7.0: 0 0 0 0
Еще пример магического поведения в старых версиях:
$a = [1,2,3]; foreach( $a as $v ) { echo current($a) . " "; } $a = [1,2,3]; $b = $a; foreach( $a as $v ) { echo current($a) . " "; } // PHP 5.6: 2 2 2 1 1 1 // PHP 7.0: 1 1 1 1 1 1
foreach всегда работает с копией массива, т.е. результат foreach не будет меняться при изменении оригинального массива внутри foreach:
$arr = [ 1, 2, 3, 4 ]; foreach( $arr as $val ){ echo "$val "; unset( $arr[1] ); } // PHP 5.6: 1 2 3 4 // PHP 7.0: 1 2 3 4
ОДНАКО, если значение массива передается по ссылке, то foreach всегда работает с исходным массивом, т.е. изменение массива внутри foreach изменит и результат foreach:
$arr = [ 1, 2, 3, 4 ]; foreach( $arr as & $val ){ echo "$val "; unset( $arr[1] ); } // PHP 5.6: 1 3 4 // PHP 7.0: 1 3 4
$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 func()
— делегирование генераторов
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');
—