Чего ожидать от PHP7. Часть 1

Совершено независимо от того как вы относитесь к PHP 7, он будет выпущен в этом году. Рабочее предложение по PHP 7 прошло практически единогласно (32 к 2). Теперь набор функций уже зафиксирован и мы увидим первый релиз уже в середине июня.

Но что это значит для конечного пользователя? Все мы видели с каким нежеланием веб хостинги переходили на PHP 5.х. Сколько мы увидим новых проблем связанных с обратной совместимостью и насколько это замедлит переход на последнюю версию?

На этот вопрос однозначного ответа нет. Читайте дальше.

Было решено несколько серьезных проблем языка. В дополнению к этому основной целью релиза стали повышение производительности и решение проблем совместимости. Углубимся в детали.

Решение проблем совместимости

Самым важным (и более незаметным) дополнением стало введение абстрактного синтаксического дерева (AST) — промежуточное представление кода в процессе компиляции (разделение парсера и компилятора). Имея такой инструмент мы сможем не только костыли с проблемных участков, но и создавать более производительный opcode.

Второй важный пункт — введение универсального синтаксиса для переменных. Именно он и может добавить разработчикам головной боли. Это дополнение решает многочисленные несоответствия, связанных с вычислением значений переменных. Например, получить значение свойства можно как при помощи ($object->closureProperty)(), так и путем цепочки статичных вызовов:

<?php
class foo { static $bar = 'baz'; }
class baz { static $bat = 'Hello World'; }
baz::$bat = function () { echo "Hello World"; };
$foo = 'foo';
($foo::$bar::$bat)();

Так же изменения коснулись семантики. В частности переменных-переменных/свойств.

До PHP 7, $obj->$properties['name'] давало доступ к свойству чьё имя содержится в ключах массива $properties. При работе с универсальным синтаксисом переменных ключ name даст вам доступ к свойству чьё имя содержится в $properties.

Рассмотрим на примере:

<?php
$obj->$properties['name']

В PHP 5.6 эта строка была бы обработана следующим образом:

<?php
$obj->{$properties['name']}

а в PHP 7:

<?php
{$obj->$properties}['name']

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

Производительность

Одна из основных причин перехода на PHP 7 — это значительное повышение производительности за счет применения phpng. Такой прирост производительности должен помочь небольшим хостингам, так как они смогут принять на себя большее число клиентов не внося никаких изменений в оборудование.

На данный момент, в зависимости от того чей тест производительности вы увидите, производительность PHP 7 стала соизмерима с Facebook HHVM, который использует JIT-компиляцию. Именно она компилирует команды PHP до уровня машинных команд (при возможности).

Не смотря на то, что JIT-компилятор очень много обсуждался, он не был включен в PHP 7. Конечно, нельзя сказать наверняка насколько повысилась бы производительность, если бы было принято иное решение, но было бы довольно интересно взглянуть на результаты, если вдруг кто-то решит создать подобный компилятор!

В дополнение к производительности, в PHP 7 улучшен процесс работы с памятью путем оптимизации структуры внутренних данных. А такая оптимизация, как правило, оказывает немалое влияние на производительность.

Проблемы обратной совместимости

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

Но, хотелось бы отметить, что почти все эти проблемы малозначительны, как например ошибки при обращении к методу не объекта, за исключением проблем в универсальным синтаксисом переменных:

<?php
set_error_handler(function($code, $message) {
   var_dump($code, $message);
});
$var = null;
$var->method();
echo $e->getMessage(); // Fatal Error: Call to a member function method() on null
echo "Hello World"; // Still runs

Были удалены ASP и script теги, то есть вы больше не сможете использовать теги <%, <%= и <script language=”php”> и, соответственно, их закрывающие теги.

Другой важный момент — удаление всего функционала, отмеченного как deprecated. Были удалены регулярные выражения совместимые с POSIX (ext/reg PHP 5.3) и старое расширение ext/mysql (deprecated c PHP 5.5).

Очередная проблема обратной совместимости проявилась в возможности использования нескольких default выражений в switch. До PHP 7 было допустимо:

<?php
switch ($expr) {
   default:
        echo "Hello World";
        break;
   default:
        echo "Goodbye Moon!";
        break;
}

Раньше обрабатывался только последний default. А в PHP 7 вы получите ошибку:

Fatal error: Switch statements may only contain one default clause

Новые возможности

Довольно нехотя нам приходится разбираться с проблемами обратной совместимости. Мы увидели прирост в производительности. Но, мы также получаем новый функционал. Именно все новое и доставляет удовольствие в работе с новыми программными продуктами и PHP 7 не исключение.

Тайпхинтинг по скалярным величинам и возвращаемое значение

Начнем с самого противоречивого нововведения в PHP 7 — тайпхинтинг для скалярных типов. Внедрение этого функционала было принято на грани отказа в голосовании. В последствии автор RFC покинул проект. За чем последовали несколько новых RFC и соревнующихся между собой решений по ним. Но в итоге прошел оригинальный RFC.

Для конечных пользователей это означает, что вы сможете использовать тайпхинтинг при работе со скалярными величинами. А именно: int, float, string и bool. По-умолчанию тайпхинтинг не строгий, то есть допускается применение значения другого типа. Что означает, что при передачи int(1) в функцию, которая ожидает увидеть float, то такой вызов будет сконвертирован в float(1.0). Вызвав float(1.5) в функцию, требующую int, вы получите int(1).

Приведем пример:

<?php
function sendHttpStatus(int $statusCode, string $message) {
    header('HTTP/1.0 ' .$statusCode. ' ' .$message);
}
sendHttpStatus(404, "File Not Found"); // integer and string passed
sendHttpStatus("403", "OK"); // string "403" coerced to int(403)

В дополнение к этому вы можете установить строгий вариант тайпхинтинга, указав declare(strict_types=1)в начале файла. Таким образом все функции, которые будут вызваны в этом файле будут полностью соблюдены своему объявлению. Хочу отметить, что это распространяется только на определенный файл, а не на тот, в котором описана переменная.

Если функции будет передан несоответствующий параметр, то будет вызвана ошибка:

<?php
declare(strict_types=1); // must be the first line
sendHttpStatus(404, "File Not Found"); // integer and string passed
sendHttpStatus("403", "OK");
// Catchable fatal error: Argument 1 passed to sendHttpStatus() must be of the type integer, string given

Дальше больше. PHP 7 поддерживает тайпхинтинг по возвращаемым типам. Чтобы указать возвращаемый тип, следует применять следующий синтаксис:

<?php
function isValidStatusCode(int $statusCode): bool {
   return isset($this->statuses[$statusCode]);
}

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

Комбинированный оператор сравнения

Мое любимое дополнение в PHP 7 — комбинированный оператор сравнения (<=>). Он является дополнительным к операторам больше и меньше.

Он работает как strcmp() и version_compare(), то есть возвращает -1 если левый операнд меньше, чем правый, 0 — если они равны и 1 — если левый операнд больше. Основное отличие заключается в том, что он применим к любым двум операндам.

Этот оператор часто встречается в функциях обратного вызова при работе с сортировкой данных:

<?php
// Pre Spacefaring^W PHP 7
function order_func($a, $b) {
   return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
}
// Post PHP 7
function order_func($a, $b) {
   return $a <=> $b;
}

Заключение

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

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

P.S. а пока эта статья находится в работе, поделитесь своим мнением касательно PHP 7. Расскажите, что вы ждете больше всего, а что вас разочаровало.