Это вторая часть нашей минисерии статей «Чего ждать от PHP7». Читать часть 1

Как вы наверное уже знаете, PHP7 придет в этом году! И сейчас самое время узнать что же нового он нам принесет.

В первой части данной серии мы рассмотрели некоторые наиболее важные изменения в PHP7 и две крупные новые возможности. В этой статье рассмотрим еще шесть, о которых вы точно захотите узнать.

Новый экранирующий символ для Unicode


Добавление нового escape-символа \u позволяет нам указывать специфические unicode символы внутри PHP-строк (да-да, те самые emoji и не только).

Синтаксис выглядит так — \u{CODEPOINT}, например, зеленое сердце, , может быть выражено как PHP-строка: "\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;
}

При каждой итерации будет выводиться:

  1. «Hello»
  2. " "
  3. «World!»
  4. «Goodbye»
  5. " "
  6. «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? Как вы относитесь к нововведениям? Есть ли что-то, с чем не согласны? Когда вы планируете перейти на новую версию? Напишите свои мысли по этому поводу в комментариях.