Это вторая часть нашей минисерии статей «Чего ждать от PHP7». Читать часть 1
Как вы наверное уже знаете, PHP7 придет в этом году! И сейчас самое время узнать что же нового он нам принесет.
В первой части данной серии мы рассмотрели некоторые наиболее важные изменения в PHP7 и две крупные новые возможности. В этой статье рассмотрим еще шесть, о которых вы точно захотите узнать.
Новый экранирующий символ для Unicode
Добавление нового escape-символа
\u
позволяет нам указывать специфические unicode символы внутри PHP-строк (да-да, те самые emoji и не только).Синтаксис выглядит так —
\u{CODEPOINT}
, например, зеленое сердце, ![](https://habrastorage.org/files/e1e/068/0ee/e1e0680eeb57459bba3bcc05585f51d9.png)
"\u{1F49A}"
.Оператор объединения со значением NULL (Null Coalesce Operator)
Еще один новый оператор —
??
. Он возвращает левый операнд, если он не имеет значение NULL; в противном случае возвращается правый операнд.Самое главное, что не генерирует
notice
, если левый операнд является несуществующей переменной. В отличии от короткого тернарного оператора ?:
, он работает как isset()
.Вы также можете использовать цепочки операторов, чтобы вернуть первый ненулевой из данного набора:
$config = $config ?? $this->config ?? static::$defaultConfig;
Привязка замыканий во время вызова
С PHP5.4 к нам пришли нововведения
Closure->bindTo()
и Closure::bind()
, которые позволяют изменить привязку $this
и области вызова, вместе, или по отдельности, создавая дубликат замыкания.PHP7 теперь добавляет легкий способ сделать это прямо во время вызова, связывая
$this
и область вызова с помощью Closure->call()
. Этот метод принимает объект в качестве своего первого аргумента, а затем любые другие аргументы, которые пойдут в замыкание:class HelloWorld {
private $greeting = "Hello";
}
$closure = function($whom) { echo $this->greeting . ' ' . $whom; }
$obj = new HelloWorld();
$closure->call($obj, 'World'); // Hello World
Группировка деклараций use
Если вам когда-либо приходилось импортировать много классов из одного и того же пространства имен, вы, наверное, были очень счастливы, когда IDE делала всю основную работу за вас. Для всех остальных, и для краткости, в PHP7 теперь есть возможность группировать декларирование операторов
use
. Это позволит быстрее и удобнее работать с большим количеством импортов и сделает код читаемее:// Original
use Framework\Component\SubComponent\ClassA;
use Framework\Component\SubComponent\ClassB as ClassC;
use Framework\Component\OtherComponent\ClassD;
// With Group Use
use Framework\Component\{
SubComponent\ClassA,
SubComponent\ClassB as ClassC,
OtherComponent\ClassD
};
Группировка может использоваться с константами и импортируемыми функциями, вы можете смешивать все вместе:
use Framework\Component\{
SubComponent\ClassA,
function OtherComponent\someFunction,
const OtherComponent\SOME_CONSTANT
};
Улучшение генераторов
return
в генераторах
В генераторах появились две очень интересные возможности. Первая — Generator Return Expressions, позволяющая возвращать значение после (успешного) завершения работы генератора.
До PHP7, если вы пытались что-нибудь вернуть в генераторе, это приводило к ошибке. Однако, теперь вы можете вызвать
$generator->getReturn()
, чтобы получить возвращаемое значение.Если генератор еще не завершился или выбросил непойманное исключение, вызов
$generator->getReturn()
сгенерирует исключение.Если же генератор завершен, но не объявлен
return
, то метод вернет NULL
.Пример:
function gen() {
yield "Hello";
yield " ";
yield "World!";
return "Goodbye Moon!";
}
$gen = gen();
foreach ($gen as $value) {
echo $value;
}
// Outputs "Hello" on iteration 1, " " on iterator 2, and "World!" on iteration 3
echo $gen->getReturn(); // Goodbye Moon!
Делегирование генератора
Вторая особенность является гораздо более захватывающей: делегирование генератора. Это позволяет вернуть другую итерабельную структуру — будь то массив, итератор или другой генератор.
Важно понимать, что итерации суб-структур осуществляются именно через самую внешнюю петлю, как если бы это была одна плоская структура, а не рекурсивный вызов.
Это утверждение также справедливо при отправке данных в генератор или выбросе исключений. Они передаются в суб-структуру, как если бы это был ее непосредственный вызов.
Синтаксис такой —
yield from <expression>
. Посмотрим на примере:function hello() {
yield "Hello";
yield " ";
yield "World!";
yield from goodbye();
}
function goodbye() {
yield "Goodbye";
yield " ";
yield "Moon!";
}
$gen = hello();
foreach ($gen as $value) {
echo $value;
}
При каждой итерации будет выводиться:
- «Hello»
- " "
- «World!»
- «Goodbye»
- " "
- «Moon!»
Стоит упомянуть еще один нюанс: поскольку суб-структуры привносят свои собственные ключи, вполне возможно, что один и тот же ключ будет возвращен за несколько итераций. Недопущение подобного — это ваша ответственность, конечно же, если для вас это важно.
\EngineException
Обработка фатальный и catchable фатальных ошибок в PHP традиционна была невозможна, или по крайней мере очень сложна. Но с добавлением Engine исключений, многие из этих ошибок будут теперь выбрасывать исключение вместо самой ошибки.
Теперь, когда фатальная или catchable фатальная неустранимая ошибка возникнут, выбросится исключение, позволяющее обработать ошибку корректно. Если его не трогать, то это приведет к традиционной фатальной ошибке необработанного исключения.
Эти исключения являются
\EngineException
объектами, и в отличии от всех пользовательских исключений, они не наследуются от базового класса \Exception
. Это сделано специально, чтобы существующий код, который ловит класс \Exception
не отлавливал и фатальные ошибки, изменяя свое поведение. Таким образом сохраняется обратная совместимость.В будущем, если вы хотите поймать как традиционные исключения, так и engine исключения, вам нужно будет отлавливать их новый общий родительский класс
\BaseException
.Кроме того, ошибки парсинга в выполняемом функцией
eval()
коде теперь будут выбрасывать \ParseException
, а несоответствие типов приведет к \TypeException
.Пример:
try {
nonExistentFunction();
} catch (\EngineException $e) {
var_dump($e);
}
object(EngineException)#1 (7) {
["message":protected]=>
string(32) "Call to undefined function nonExistantFunction()"
["string":"BaseException":private]=>
string(0) ""
["code":protected]=>
int(1)
["file":protected]=>
string(17) "engine-exceptions.php"
["line":protected]=>
int(1)
["trace":"BaseException":private]=>
array(0) {
}
["previous":"BaseException":private]=>
NULL
}
Скоро!
РНР 7.0.0 исполнилось всего восемь месяцев, и, вполне возможно, это будет самым быстрым релизом мажорной версии в истории PHP. Пока все еще в альфа-версии, но уже сейчас все складывается очень хорошо.
И вы можете помочь сделать еще лучше.
Проверь свой код
Возьмите PHP7 vagrant box от Расмуса и запустите ваши тесты или проверьте по своему чек-листу ваше приложение. Сообщите о багах в проект, повторяйте регулярно :)
Помоги GoPHP7-ext
Одним из основных препятствий для PHP7 является большое количество работы по обновлению всех расширений для работы с новым Zend Engine 3.
Если вы используете расширение, которое не слишком популярно и известно, и вам не нравится уровень его поддержки, или же у вас есть свои собственные расширения — посмотрите на проект GoPHP7-ext и примите участие, перенеся свои расширения на новый движок.
Документация
Каждая новая фича PHP7 имеет свой RFC. Все они могут быть найдены в вики PHP.net и являются хорошей отправной точкой для написания новой документации. Вы можете сделать это онлайн в GUI среде, в том числе и закоммитить (если у вас есть карма) или отправить патч на проверку.
Заключение
РНР 7 будет великим!
Протестируйте ваши приложения. Помогите перенести расширения.
P.S. вы уже пробовали PHP7? Как вы относитесь к нововведениям? Есть ли что-то, с чем не согласны? Когда вы планируете перейти на новую версию? Напишите свои мысли по этому поводу в комментариях.