Готовимся к PHP 7

uBsLNS0 - Imgur

2015 был важным годом для PHP. Спустя одиннадцать лет после релиза 5.0 версии, новая мажорная (основная) версия наконец ступает на наш путь! PHP 7 запланирован к выпуску ближе к концу года, принеся много новых возможностей языка и впечатляющее повышение производительности.

Но как это повлияет на вашу текущую кодовую базу PHP? Что действительно изменилось? Насколько безопасно обновление? Статья ответит на эти вопросы и даст вам вкус того, что должно придти с PHP 7.

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

Производительность является, несомненно, самой большой причиной, почему вы должны обновить свои серверы, как только стабильная версия будет выпущена. Рефакторинг ядра, представленный phpng RFC, даёт работать PHP 7 с такой скоростью, как (или быстрее, чем) HHVM. Официальные сравнительные тесты являются впечатляющими: большинство приложений из реального мира, работающих на PHP 5.6, будут работать по крайней мере вдвое быстрее на PHP 7.

Для подробных сравнительных тестов производительности взгляните на презентацию Рэсмуса Лердорфа в PHP Австралия. (Вы можете использовать клавиши со стрелками для навигации слайдов.) Вот некоторые сравнительные тесты WordPress из той презентации:

php7_graph-c863bf78PHP 7 обрабатывает больше чем в два раза запросов в секунду, что на практике будет означать 100%-ое улучшение производительности для веб-сайтов на WordPress.

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

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

Удалены устаревшие (deprecated) элементы

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

В частности, теги в стиле ASP (<%, <% = и %>) были удалены вместе с тегами script (<script language=»php»>). Удостоверьтесь, что вы используете рекомендуемый тег <?php вместо них. Другие функции, ранее устаревшие, такие как split, были также удалены в PHP 7.

Расширение ereg (и все функции ereg_*) устарело, начиная с PHP 5.3. Оно должно быть заменено расширением PCRE (preg_* функции), которое предлагает еще много других функций. Mysql расширение (и функции mysql_*) устарело, начиная с PHP 5.5. Для прямой миграции Вы можете использовать mysqli расширение и функции mysqli_* вместо них.

Универсальный синтаксис переменных

Универсальный синтаксис переменных предназначен для решения серии несоответствий при оценке переменная-переменной выражений. Рассмотрите следующий код:

<?php
class Person
{
   public $name = 'Erika';
   public $job = 'Developer Advocate';
}

$person = new Person();
$property = [ 'first' => 'name', 'second' => 'info' ];
echo "\nMy name is " . $person->$property['first'] . "\n\n";

В PHP 5 выражение $person->$property[‘first’] оценено как $person->{$property[‘first’]}. На практике это будет интерпретироваться как $person->name, давая Вам результат «My name is Erika». Даже при том, что это — крайний случай, он показывает ясные несоответствия с нормальным порядком вычисления выражения, которым является слева направо.

В PHP 7 выражение $person->$property[‘first’] оценено как {$person->$property}[‘first’]. Интерпретатор оценит $person->$property сначала; следовательно, предыдущий пример кода не будет работать в PHP 7, потому что $property является массивом и не может быть преобразован в строку.

Быстрым и простым способом решить эту проблему можно путем явного определения порядка оценки с помощью изогнутых фигурных скобок (например, $person->{$property[‘first’]}), который гарантирует то же поведение и на PHP 5 и на PHP 7.

Благодаря новой универсальной форме слева направо синтаксиса переменных, множество выражений, ранее обработанных как недопустимые, теперь станут допустимыми. Для иллюстрирования этого нового поведения рассмотрите следующий класс:

<?php
class Person
{
   public static $company = 'DigitalOcean';
   public function getFriends()
   {
       return [
           'erika' => function () {
               return 'Elephpants and Cats';
           },
           'sammy' => function () {
               return 'Sharks and Penguins';
           }
       ];
   }

   public function getFriendsOf($someone)
   {
       return $this->getFriends()[$someone];
   }

   public static function getNewPerson()
   {
       return new Person();
   }
}

С PHP 7 мы можем создать вложенные ассоциации и различные комбинации между операторами:

$person = new Person();
echo "\n" . $person->getFriends()['erika']() . "\n\n";
echo "\n" . $person->getFriendsOf('sammy')() . "\n\n";

Этот отрывок дал бы нам ошибку анализа в PHP 5, но сработал как ожидалось в PHP 7.

Точно так же возможен вложенный статический доступ:

echo "\n" . $person::getNewPerson()::$company . "\n\n";

В PHP 5 это дало бы нам классическую синтаксическую ошибку T_PAAMAYIM_NEKUDOTAYIM.

Фатальная ошибка с несколькими default переключателями

Это, снова, крайний случай, и он больше связан с логическими ошибками в вашем коде. Нет смысла в нескольких условиях по умолчанию в switch, но это никогда не вызывало исключений, поэтому трудно было обнаружить ошибку. В PHP 5 использовалось бы последнее значение по умолчанию, но в PHP 7 вы теперь получите Fatal Error: Switch statements may only contain one default clause.

Исключения движка

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

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

<?php
set_error_handler(function ($code, $message) {
   echo "ERROR $code: " . $message . "\n\n";
});

function a(ArrayObject $b){
   return $b;
}

a("test");

echo "Hello World";

Этот код генерирует исправимую ошибку, вызванную несоответствием типов при вызывании функции a(), использованием строки как параметр. В PHP 5 это генерирует E_RECOVERABLE, пойманный пользовательским обработчиком ошибок, таким образом, вы получите:

ERROR 4096: Argument 1 passed to a() must be an instance of ArrayObject, string given, called in /data/Projects/php7dev/tests/test04.php on line 12 and defined(...)

Hello World

Заметьте, что выполнение продолжается, потому что ошибка была обработана. В PHP 7 этот код генерирует исключение TypeError (не ошибка!), таким образом, пользовательский обработчик ошибок не вызовется. Вы получите:

Fatal error: Uncaught TypeError: Argument 1 passed to a() must be an instance of ArrayObject, string given, called in /vagrant/tests/test04.php on line 12 and defined in /vagrant/tests/test04.php:7
Stack trace:
#0 /vagrant/tests/test04.php(12): a('test')
#1 {main}
  thrown in /vagrant/tests/test04.php on line 7

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

  • Throwable interface
    • Exception implements Throwable
      • ErrorException extends Exception
      • RuntimeException extends Exception
    • Error implements Throwable
      • TypeError extends Error
      • ParseError extends Error
      • AssertionError extends Error

Это в основном означает, что новым всеобъемлющим исключением является теперь Throwable вместо Exception. Это не должно влиять ни на какой устаревший код, но нужно помнить об этом при обработке новых исключений механизма в PHP 7.

Новые функции языка

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

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

PHP 7 идет с двумя блестящими новыми операторами: spaceship (или объединенный оператор сравнения) и null coalesce operator.

Оператор spaceship (<=>), также известный как объединенный оператор сравнения, может использоваться для создания цепочечного сравнения более коротким. Рассмотрите следующее выражение:

$a <=> $b

Это выражение оценит к -1, если $a будет меньшим, чем $b, 0, если $a равняется $b, и 1, если $a больше, чем $b. Это в основном краткая запись следующего выражения:

($a < $b) ? -1 : (($a > $b) ? 1 : 0)

Null coalesce operator (??) также работает как краткая запись случая общего использования: условное приписывание, проверяющее, установлено ли значение перед использованием его. В PHP 5 вы обычно делали бы что-то вроде этого:

$a = isset($b) ? $b : "default";

С null coalesce operator в PHP 7, мы можем просто использовать:

$a = $b ?? "default";

Скалярные подсказки типа

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

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

<?php
function double(int $value)
{
   return 2 * $value;
}
$a = double("5");
var_dump($a);

Этот код не генерирует ошибок, потому что мы не используем строгий режим. Единственной вещью, которая произойдет, является преобразование типов, таким образом, строка «5» принята, поскольку параметр будет приведён в целое число в double функции.

Если мы хотим удостовериться, что только целым числам позволяют быть переданными double функции, мы можем включить строгий режим указанием директивы declare(strict_types = 1) как самой первой строкой в нашем скрипте:

<?php
declare(strict_types = 1);
function double(int $value)
{
   return 2 * $value;
}
$a = double("5");
var_dump($a);

Этот код генерирует Fatal Error: Uncaught TypeError: Argument 1 passed to double() must be of the type integer, string given.

Подсказки типа возврата

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

<?php
function a() : bool
{
   return 1;
}
var_dump(a());

Этот отрывок будет работать без предупреждений, и возвращенное значение будет преобразовано в bool автоматически. При включении строгого режима (также, как со скалярными подсказками типа), вы получите фатальную ошибку вместо этого:

Fatal error: Uncaught TypeError: Return value of a() must be of the type boolean, integer returned

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

Что дальше

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

Обратите внимание на то, что даже притом, что никакие новые функции не будут включены, некоторые изменения могут все еще произойти перед заключительным выпуском, поэтому просто еще не используйте PHP 7 в продакшне! Существует Vagrant VM, разработанный и опубликованный Рэсмусом Лердорфом, который Вы можете использовать для тестирования текущего кода PHP 7. Вам настоятельно рекомендуется проверить свои приложения и сообщать о любых проблем, которые сможете найти.

Счастливого кодирования!


 

Примечание

Эта перевод статьи «Getting Ready for PHP 7».

Титульное изображение взято с сайта http://imgur.com/a/q6i58.

Комментарии

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *