Что нового в 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');
—
