Чего ожидать от PHP 7, часть 2

В первой части мы рассмотрели наиболее важные аспекты обратной совместимости и новый функционал в PHP 7. В этой же статье мы рассмотрим 6 других нововведений в PHP 7, о которых, однозначно, стоит знать.

Синтаксис для использования символов юникода

Новый экранирующий символ \u позволит указывать символы юникода в шестнадцатеричном формате внутри строк PHP:

Формат использования имеет следующий вид: \u{CODEPOINT}, например символ зеленого сердца можно напечатать так: `\u{1F49A}.

Объединенный null оператор

Следующая новинка — объединенный оператор null - ??. Он возвращает левый операнд если он не null, а в противном случае возвращает правый.

Стоит отметить, что в случае если левый операнд — не объявленная ранее переменная, ошибка не возникает. Поведение этого оператора идентично isset() и полностью противоположно тернарному оператору ?:.

Так же при помощи нового оператора можно строить цепочки, в результате которых будет возвращен первый не null оператор:

<?php
$config = $config ?? $this->config ?? static::$defaultConfig;

Привязка замыкания при вызове

Начиная с PHP 5.4 мы видели новый метод Closure->bindTo()|Closure::bind(). Он позволяет изменить область видимости и значение переменной $this, по отдельности или вместе через создание копии замыкания.

В PHP 7 этот процесс значительно упрощен, привязка к области видимости, и $this одного объекта происходит через метод Closure->call(). Он принимает сам объект в качестве первого аргумента, а все остальные параметры передаются замыканию:

<?php
class HelloWorld
{
    private $greeting = "Hello";
}
$closure = function($whom) {
    echo $this->greeting . ' ' . $whom;
}
$obj = new HelloWorld();
$closure->call($obj, 'World'); // Hello World

Группировка объявлений

Наверняка, вы оценили возможность продвинутых IDE автоматически добавлять use объявления, если вам приходилось импортировать много классов в проект. Теперь PHP 7 позволяет использовать групповые объявления. Они помогут импортировать несколько классов в упрощенном виде:

<?php
// 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
};

Такой подход применим не только при импортировании констант и функций, но также и при смешанных импортах:

<?php
use Framework\Component\{
    SubComponent\ClassA,
    function OtherComponent\someFunction,
    const OtherComponent\SOME_CONSTANT
};

Улучшение генераторов

Возвращаемое значение генератора

В генераторы было добавлено две новых функции. Первая — возвращаемое выражение генератора, которая указывает возвращаемое значение в случае удачного выполнения генератора.

До PHP 7, если вы пытались вернуть что-то из генератора, то возникала ошибка. А теперь вы можете вызвать метод $generator->getReturn() для этой цели.

Если генератор не успел завершить свою работу или его исполнение прервалось в силу возникшего исключения, то обращение к методу $generator->getReturn() также вызовет исключение.

Если же генератор удачно отработал, а возвращать ему нечего, то вернется null.

Пример:

<?php
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 <выражение>, например:

<?php
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!"

Следует помнить, что так как субструктуры могут отдавать свои ключи, то вполне возможно, что эти ключи будут повторятся в разных итерациях. Поэтому, если для вас ключи имеют значение, то следует принять некоторые меры для устранения такой проблемы.

Исключения

Обработка критичных и фатальных ошибок в PHP традиционно либо вообще не поддавалось обработке, либо было довольно затруднительно это сделать. Благодаря новому движку исключений много из таких ошибок теперь будут вызывать исключения.

То есть теперь при возникновении фатальных или улавливаемых ошибок будет вызвано исключение, которое вы можете легко обработать. Если же вы не обработаете его, то будет вызвана та же самая ошибка.

Такие исключения это объекты класса EngineExceptions, которые в отличии от всех остальных исключений не наследуют класс Exception. Это было сделано специально, чтобы старый код не начал отлавливать этот вид исключения, если, например, вы использовали отлавливание исключений типа \Exception, что помогает поддерживать обратную совместимость.

В будущем, если вы хотите отловить абсолютно все виды исключений, то вам следует использовать новый класс \BaseException.

В дополнение ко всему отметим, что код внутри eval() будет вызывать исключение типа \ParseException, а несоответствии типов вызовет `\TypeException.

Пример:

<?php
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
}

Скоро!

До выпуска PHP 7.0.0 осталось 8 месяцев. Таким образом эта версии может стать самой быстрой основной выпущенной версией PHP. Хотя она все еще в альфа стадии, набирает обороты довольно быстро.

И вы сами можете помочь PHP развиваться.

Тестирование собственного кода

Загрузите PHP 7 vagrant box от Rasmus и проведите тестирование своего проекта на нем. Сообщети о возможных проблемах и продолжайте регулярно тестировать.

Помогайте с GOPHP7-EXT

Одной из основных проблем остается совместимость всех расширений с новым Zend Engine 3. Если вы применяете какое либо мало известное расширение, или разработчики которого прекратили его поддержку, то обратите внимание на проект GoPHP7-ext. Он поможет сделать так, что все расширения будут работать с первого для выпуска нового релиза PHP.

Пишите документацию

Каждая новая функция в PHP имеет соответствующий RFC. Все их вы можете найти на PHP.net wiki, именно оттуда и стоит начинать писать новые участки документации. Вы можете это делать при помощи online GUI, делая комиты прямо оттуда, или передавать патчи на одобрение.

Заключение

PHP 7 должен быть отличным!

Тестируйте свои приложения. Помогайте в проверки расширений.

P.S.: вы уже пробовали работать с PHP7? Как вам нововведения? Вы с чем-то не согласны или считаете, что следовало добавить еще что-то? Когда вы планируете полностью перейти на новую версию PHP? Делитесь своим мнением!