PHP 7: Что нового. часть 1

Категория: / DEV Блог / PHP (LAMP)
Релиз PHP 7 запланирован на конец ноября 2015 года.
В новой версии PHP появились интересные синтаксические плюшки, новые функции
а также самые долгожданные изменения коснулись ядра интерпретатора - проект phpng, увеличивает скорость
обработки скриптов практически вдвое по сравнению с PHP 5.x, плюс более эффективный менеджер памяти.



бенчмарк PHP7

Итак, что изменилось?

Combined Comparison Operator


Объединенный оператор сравнения (spaceship operator - корабль) - шоткат для операция трехстороннего сравнения двух операндов.
Записывается в форме x = a <=> b. Возвращает одно из трех значений:


1 (если a > b)
0 (a = b)
-1 (a < b)



Оператор имеет одинаковый приоритет с операциями сравнения (`==`, `!=`, `===`, `!==`) и работает также как и операторы (`<`, `>=`, ...).
Также корабль не ассоциативен, то есть вы НЕ МОЖЕТЕ связать его с предыдущим вызовом (например `1 <=> 2 <=> 3`).

var_dump('PHP' <=> 'Node'); // int(1)
var_dump(123 <=> 456); // int(-1)
// сравниваются элементы массива по порядку
var_dump(['a', 'b'] <=> ['a', 'b']); // int(0)


Объекты данным оператором сравнивать нельзя.

RFC: Combined Comparison Operator

?? - Оператор объединения со значением NULL


Оператор является сокращенным вариантом вызова isset в тернарном операторе ?:.

Пример

// до PHP7
$route = isset($_GET['route']) ? $_GET['route'] : 'index';
// или
$route = @$_GET['route']) ?: 'index';
 
// PHP 7+
$route = $_GET['route'] ?? 'index';


Null Coalesce Operator

Скалярные типы


Теперь в объявлениях переменных можно указывать их тип, даже если они являются скалярными (числа и строки).
По умолчанию используется кастинг для приведения к заданному типу, если вы хотите имееть более жесткий контроль,
можно включить strict режим, тогда при присвоении переменной значения, тип которого отличается от объявленного,
будет выброшено исключение TypeError.

Типы:

строки (`string`),
целые (`int`),
числа (`float`),
бульки (`bool`).
 
Типы аргументов, которые появились в php 5.x
 
названия классов,
интерфейсы,
массив `array`
и выполняемые `callable` аргументы.


Пример

// Coercive mode
function sumOfInts(int ...$ints)
{
    return array_sum($ints);
}
 
var_dump(sumOfInts(2, '3', 4.1)); // int(9)


Чтобы включить строгий strict режим проверки, нужно в начале (каждого) файла, добавить вызов

declare(strict_types=1);

Этот режим помимо обработки параметров влияет на возвращаемые значения!
В строгом режиме запрещен любой кастинг кроме конвертации int => float (но не наоборот)

declare(strict_types=1);
 
function multiply(float $x, float $y)
{
    return $x * $y;
}
 
function add(int $x, int $y)
{
    return $x + $y;
}
 
var_dump(multiply(2, 3.5)); // float(7)
var_dump(add('2', 3)); // Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type integer, string given...


Важно: Strict mode работает только по месту вызова функции, то есть если описание функции находится в файле с объявлением strict_types=1,
а вызывается она из другого файла без strict_types, то никакой строгой типизации не будет!

Необратимые изменения
Классы с именами `int`, `string`, `float`, and `bool` теперь запрещены.
Любое использование классов с этими названиями приведет к фатальным ошибкам.

Scalar Type Declarations

Объявления возращаемых типов


Добавлена возможность указать тип переменной, возвращаемой функцией или методом или замыканием.

Следующие типы поддерживаются:

`string`, `int`, `float`, `bool`, `array`, `callable`, `self` (в методах), `parent` (methods only) , `Closure`,
 имя класса, имя интерфейса


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
)


При использовании наследования в классах, дочерние методы должны соблюдать объявления возвращаемых типов
указанные в родительском классе / интерфейсе

class A {}
class B extends A {}
 
class C
{
    public function test() : A
    {
        return new A;
    }
}
 
class D extends C
{
    // overriding method C::test() : A
    public function test() : B // Fatal error due to variance mismatch
    {
        return new B;
    }
}


Объявление метода `D::test() : B` вызовет `E_COMPILE_ERROR` потому что многовариантность не дозволена.
Для того чтобы метод `D::test()` заработал, нужно указать что он возвращает нечто типа `A`.

class A {}
 
interface SomeInterface
{
    public function test() : A;
}
 
class B implements SomeInterface
{
    public function test() : A // Окей
    {
        return null; // Fatal error: Uncaught TypeError: Return value of B::test() must be an instance of A, null returned...
    }
}


В этот раз выполнение закончится эксепшном `TypeError`, потому что `null` не валидный возвратный тип, валидацию пройдет только экземпляр клааса `A`.

Return Type Declarations

Анонимные классы. Да!


Аноимные классы подходят для небольших задач. Из часто используют в языках например C# или Java для выполнения колбэков.


// до PHP 7
class Logger
{
    public function log($msg)
    {
        echo $msg;
    }
}
 
$util->setLogger(new Logger());
 
// PHP 7+
$util->setLogger(new class {
    public function log($msg)
    {
        echo $msg;
    }
});


Анонимные классы позволяют делать тоже самое что и обычные классы: передавать данные в конструктор, наследовать другие классы, использовать трейты и т.п.

class SomeClass {}
interface SomeInterface {}
trait SomeTrait {}
 
var_dump(new class(10) extends SomeClass implements SomeInterface {
    private $num;
 
    public function __construct($num)
    {
        $this->num = $num;
    }
 
    use SomeTrait;
});
 
# выведет
 
object(class@anonymous)#1 (1) {
  ["Command line code0x104c5b612":"class@anonymous":private]=>
  int(10)
}


Анонимные классы поддерживают вложенность. Вложенные классы не будут иметь доступа к private/protected свойствам внешнего класса.
Если такое необходимо, то нужно передать эти данные через конструктор вложенного класса.

<?php
 
class Outer
{
    private $prop = 1;
    protected $prop2 = 2;
 
    protected function func1()
    {
        return 3;
    }
 
    public function func2()
    {
        return new class($this->prop) extends Outer {
            private $prop3;
 
            public function __construct($prop)
            {
                $this->prop3 = $prop;
            }
 
            public function func3()
            {
                return $this->prop2 + $this->prop3 + $this->func1();
            }
        };
    }
}
 
echo (new Outer)->func2()->func3(); // 6


Anonymous Classes

Поддержка юникод управляющих (escape-) последовательностей


Теперь в "строках" и объявлениях heredoc можно использовать \uXXXX последовательности для описания символов юникод

echo "\u{aa}"; // &#170;
echo "\u{0000aa}"; // &#170; (ведущие ноли можно опустить)
echo "\u{9999}"; // &#39321;


Unicode Codepoint Escape Syntax

Метод call() у замыканий


Новый метод `call()` - шоткат для выполнения лямбды с прикрепленным объектом выполнения.

class A {private $x = 1;}
 
// до PHP 7
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, 'A'); // биндим А к лямбде
echo $getX(); // 1
 
// PHP 7+
$getX = function() {return $this->x;};
echo $getX->call(new A); // 1


Closure::call

Фильтрация `unserialize()`


Новый функционал направлен на обеспечение безопасности сериализации объектов, предотвращает возможные code injection атаки,
давая возможность разработчику указать какие классы могут обрабатываться, а какие игнорироваться.

// конвертирует все объекты в __PHP_Incomplete_Class
$data = unserialize($foo, ["allowed_classes" => false]);
 
// конвертирует все объекты в __PHP_Incomplete_Class object исключая MyClass и MyClass2
$data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]);
 
// поведение по-умолчанию, разрешает все классы
$data = unserialize($foo, ["allowed_classes" => true]);


Filtered unserialize()

Класс IntlChar


Новый класс IntlChar расширяет функциональность ICU (библиотека для локализации приложений International Components for Unicode)
Класс содержит статические методы и константы для работы с юникодом.

printf('%x', IntlChar::CODEPOINT_MAX); // 10ffff
echo IntlChar::charName('@'); // COMMERCIAL AT (sign)
var_dump(IntlChar::ispunct('!')); // bool(true)


Для использования должно быть установлено расширение Intl.

Необратимые изменения
Классы в глобальном пространстве не могут называться IntlChar.

IntlChar class

Ожидания - Expectations


Обратно совместимое расширение для классической функции `утверждения` (assert).

void assert (mixed $expression [, string|Exception $message]);


Если первый параметр функции равен false, то бросается исключение / выводится ошибка с AssertionError (второй параметр).

Assert позволяет отлаживать скрипты, при этом в продакш окружении вызовы будут игнорировать интерпритатором, т.е. ничего не стоить в плане ресурсов.

ini_set('assert.exception', 1);
 
class CustomError extends AssertionError {}
 
assert(false, new CustomError('Что-то пошло не так'));


Добавлены две настройки PHP.ini, по-умолчанию:

- zend.assertions = 1
 - assert.exception = 0
 
#zend.assertions может быть равен:
 1 = обрабатывать утверждения assert (режим разработки | development mode)
 0 = генерировать код, но не выполнять
-1 = игнорировать код (0-стоимость, production mode)
 
#assert.exception
Включает возможность генерирования исключения вместо вывода ошибки и остановки скрипта,
выключена для обратной совместимости со старым `assert()`


Expectations

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


Если у импортируемых классов общее пространство имен, можно объединить их импорт в группу
(это относится к классам, функциям, константам, интерфейсам)

// до 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;
 
// PHP 7+
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};


Group use Declarations

Возврат выражений в генераторах

Generator Return Expressions
Позволяет использовать `return (expression)` в генераторах.
Значение может быть получено вызовом метода `Generator::getReturn()`, только по завершении работы генератора.

$gen = (function() {
    yield 1;
    yield 2;
 
    return 3;
})();
 
foreach ($gen as $val) {
    echo $val, PHP_EOL;
}
 
echo $gen->getReturn(), PHP_EOL;
 
// вывод:
 1
 2
 3


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

Generator Return Expressions

Делегирование генераторов

Generator Delegation
Построено на основе возможности возврата выражений из генератора.
Используется новый синтаксис:

yield from <expr>
# где <expr> может быть объектом Traversable или массивом


<expr> будет вызываться до тех пор, пока возвращает данные, затем выполнения продолжится в вызывающем генераторе.
Это позволяет разбить сложный генератор на несколько маленьких - что способствует более чистому коду и его реюзабельности.

function gen()
{
    yield 1;
    yield 2;
 
    return yield from gen2();
}
 
function gen2()
{
    yield 3;
 
    return 4;
}
 
$gen = gen();
 
foreach ($gen as $val)
{
    echo $val, PHP_EOL;
}
 
echo $gen->getReturn();
 
// вывод
 1
 2
 3
 4


Generator Delegation

Целочисленное деление intdiv()


Функция intdiv() позволяет делить числа на выходе получая целые числа.

var_dump(intdiv(10, 3)); // int(3)


Необратимые изменения
Ключевое слово `intdiv` зарезервировано для глобального контекста.

[purl=https://wiki.php.net/rfc/intdivintdiv()[/purl]

Параметры session_start()


Теперь возможно передавать параметры функции session_start().
Опции - стандартные настройки сессий из php.ini.

session_start(['cache_limiter' => 'private']); // устанавливает <a href="/r.php?url=http://php.net/manual/ru/function.session-cache-limiter.php" title="session.cache_limiter (http://php.net/manual/ru/function.session-cache-limiter.php)" class="postlink" target="_blank">session.cache_limiter</a> в private


Добавился новая настройка для сессий `session.lazy_write`, по-умолчанию включена.

Introduce session_start() Options

Функция preg_replace_callback_array()


Альтернатива preg_replace_callback, когда требуется обработать несколько условий в коллбеке.
Позволяет передать в качестве обратной функции - массив ['/regex'/ => callback, ...].

string preg_replace_callback_array(array $regexesAndCallbacks, string $input);


$tokenStream = []; // [tokenName, lexeme] pairs
 
$input = <<<'end'
$a = 3; // variable initialisation
end
;
 
// до PHP 7
preg_replace_callback(
    [
        '~\$[a-z_][a-z\d_]*~i',
        '~=~',
        '~[\d]+~',
        '~;~',
        '~//.*~'
    ],
    function ($match) use (&$tokenStream) {
        if (strpos($match[0], '$') === 0) {
            $tokenStream[] = ['T_VARIABLE', $match[0]];
        } elseif (strpos($match[0], '=') === 0) {
            $tokenStream[] = ['T_ASSIGN', $match[0]];
        } elseif (ctype_digit($match[0])) {
            $tokenStream[] = ['T_NUM', $match[0]];
        } elseif (strpos($match[0], ';') === 0) {
            $tokenStream[] = ['T_TERMINATE_STMT', $match[0]];
        } elseif (strpos($match[0], '//') === 0) {
            $tokenStream[] = ['T_COMMENT', $match[0]];
        }
    },
    $input
);
 
// PHP 7+
preg_replace_callback_array(
    [
        '~\$[a-z_][a-z\d_]*~i' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_VARIABLE', $match[0]];
        },
        '~=~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_ASSIGN', $match[0]];
        },
        '~[\d]+~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_NUM', $match[0]];
        },
        '~;~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_TERMINATE_STMT', $match[0]];
        },
        '~//.*~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_COMMENT', $match[0]];
        }
    ],
    $input
);


Необратимые изменения
Имя `preg_replace_callback_array` зарезервировано.


Add preg_replace_callback_array Function



PHP7 Что нового - продолжение

@Ссылки
Оригинал статьи