php7 изменения и производительность

Дата выхода php-7.0 все ближе и ближе. Планируемые даты релизов выглядят следующим образом:

Рассмотрим что будет из себя будет представлять новый релиз, и стоит ли переходить на php7 в будущем.

Изменения в ядре php7 (zend engine 3, phpng)

Как многие уже знают, самые значительные изменения были проведены в ядре php. Начиная с php-5.0, с каждым релизом, велись работы по улучшению производительности как самого ядра так и OPCache. И производительность действительно улучшалась как в синтетических тестах так и в реальных приложениях. Но к версии 5.5 стало ясно что "узкое место" в плане производительности - это система выделения и освобождения памяти (более подробно об этом можно почитать на php internals). Потому было принято решение переработать типизацию данных, для того чтобы найти оптимальный способ выделения памяти. В течении около пяти месяцев велась работа по рефакторингу и изменению ядра Zend Engine 2.x.

Результаты работы получили кодовое имя phpng, так же известном как "Zend Engine 3". Так как система типизации была изменена - почти все расширения (такие как mysql, ereg и другие) потребовали значительной переработки. Многие из них уже совместимы с phpng, но не все.

Следует учесть, что новое ядро в первую очередь было нацелено на улучшение производительности (и управлением памятью) у "реальных приложений" а не синтетических тестов. И судя по результатам тестов - производительность увеличилась значительно.

Из других преимуществ ядра можно отметить следующее:

Недостатки, или следствия изменений:

В данном релизе внесены значительные изменения в конструкций языка и расширения. Рассмотрим их по отдельности.

Изменения в расширениях

Все расширения были переработаны с учетом новой типизации, но изменения также коснулись и функций.

Итак, наиболее явные изменения:

Новые конструкции и изменения языка

В конструкции языка внесено множество изменений как уровня логики так и простых упрощений. Если говорить прямо, то компилятор php был полностью переписан и на данный момент использует AST для компиляции.

Наиболее очевидные изменения, того что "добавлено":

То что будет "удалено" и более не поддерживается:

Рассмотрим некоторые нововведения подробней.

Новые операторы

Оператор use стал немного умней, теперь допускается конструкция с несколькими пространствами имен, к примеру:

<?php

// было

use Hcbogdan\Blog\Post;
use Hcbogdan\Blog\Tag;
use Hcbogdan\Blog\Comment;

// стало возможным в php7

use Hcbogdan\Blog\{
  Post,
  Tag,
  Comment,
};

Добавлен оператор сравнения с null  "??". Он позволяет в некоторых случаях сделать код более читабельней, к примеру:

<?php
$foo = null;
$bar = $foo ?? 'FOO NOT SET'; // <---- FOO NOT SET

Добавлен оператор комбинированного сравнения "<=>" (T_SPACESHIP). Его суть заключается в следующем:

<?php

// было на php5.x

function customOrder($a, $b) {
  return ($a < $b)? -1: (($a>$b) ?1 :0);
}

// стало возможным в php7

function customOrderNG($a, $b) {
  return $a <=> $b;
}

Исключения (exception) на замену фатальным ошибкам (fatal error)

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

  1. BaseException
    1. ParseException
    2. EngineException
      1. TypeException
    3. Exception
      1. ErrorException
      2. RuntimeException

Существует возможность обрабатывать такие случаи:

<?php
try {
  ololo();
} catch(EngineException $e) {
  echo 'Ура - теперь мы можем отловить это';
}

Указание типа возвращаемого значения (return type declaration)

Функции могут декларировать явно тип возвращаемого значения в прототипе (с помощью языковой конструкции). В случае если возвращаемое значение не соответствует заявленному во время выполнения - происходит FatalError.

<?php
function ololo():int {
  return [];
}

ololo(); // <------- FATAL ERROR

Также можно отметить, что:

  1. такие типы как "resource" и "null" не поддерживаются
  2. в случае не совпадения типов - php пытается их привести, и только в случае неудачи происходит FatalError

Скалярные типы аргументов функции (scalar type hints)

Данная возможность позволяет указать в прототипе функции тип int, bool, string и другие. Тип null и resource  не поддерживаются.

<?php
function ololo(int $a, bool $b){ }

Если во время выполнения подобной функции передается не соответствующая  типу переменная, компилятор пытается ее преобразовать, и в случае если это не возможно - происходит FatalError.

"строгий режим" работы с типами (stitict mode)

Интерпретатор позволяет регулировать "строгость" соответствия типов данных с помощью директивы declare(strict_types=1). Данная директива позволяет включать или выключать строгий режим типов.
Есть несколько особенностей ее использования:

  1. директива declare - обязана быть первым оператором в файле.
  2. при включении "строгого режима" ( в начале файла есть оператор declare(strict_types=1) ) - данный режим включается только у того файла где есть эта директива

Пример работы в обычном режиме:

<?php
function ololo(int $a){ }
ololo( '11' );
// выполнено без ошибок

Пример работы в "строгом режиме":

<?php
declare(strict_types=1);
function ololo(int $a){ }
ololo( '11' ); // <----- FATAL ERROR

Анонимные классы

Анонимные классы похожи на аналогичные в других языках и на существующие в php5 анонимные функции.

<?php
var_dump( new ololo(){ } );  // <---- все в порядке

Изменения в интерпретации переменных

В новой версии интерпретация переменный стала следовать семантике left-to-right. Сравним некоторые случаи.

PHP5:

<?php
${$a['b']['c']}
$a->{$b['c']}
$a->{$b['c']}()
Ololo::{$b['c']}()

PHP7:

<?php
($$a)['b']['c']
($a->$b)['c']
($a->$b)['c']()
(Ololo::$a)['b']()

Изменения в некоторых языковых конструкциях

Изменено поведение list(), и именно:

Из приведения типов убрано привидение типов из строки "0xXXX" в число.

<?php
var_dump( '0x123' == '291' );
// php5: true

// php7: false

Убрана возможность повторной декларации переменных в прототипе функции:

<?php
function ololo($a, $a){ } // <-------- FATAL ERROR

Для именования классов, интерфейсов и примесей теперь нельзя использовать имена зарезервированных типов:

<?php
class String { } // <---------- FATAL ERROR

Изменено поведение побитовых операторов. Сдвиг int на количество битов большее разрядности int всегда будет возвращать 0.

<?php
var_dump( 1 << 64 );
// php5: 1

// php7: 0

Побитовый сдвиг на отрицательное число будет возвращать false и генерировать предупреждение.

<?php
var_dump( 1 >> -1 );
// php5: 0

// php7: false

Выводы

Проделанная разработчиками работа над php7 действительно огромная. Язык начинает приобретать новый, более изящный вид. Да, в этом релизе все еще существует оператор "goto" и наборы функций вроде "array_*", как и много других спорных моментов. Тем не менее, даже с учетом этого, прогресс есть :).

Как и другие major релизы, php7 ломает не мало зависимостей, но с другой стороны дает более совершенный инструмент для разработчика. По результатам тестов (синтетических и на реальных системах) мы получаем (имеются ввиду билд с HHVM):

  1. прирост производительности в 2 раза (примерно) 
  2. потребление памяти уменьшено на ~ 40% (почти в два раза)

Комментарии