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

Категория: / DEV Блог / PHP (LAMP)
PHP 7: Что нового. Часть 1.

Функции CSPRNG


Криптографически стойкий генератор псевдослучайных чисел (англ. Cryptographically secure pseudorandom number generator, CSPRNG)

Новведение добавляет две новых функции для генерации криптографически стойких целых чисел и строк.
Функции работают одинаково на всех платформах.

string random_bytes(int length);
int random_int(int min, int max);


Обе функции сгенерируют исключение `Error`, если источник необходимой случайности будет не найден.

Необратимые изменения
зарезервированные имена `random_int`, `random_bytes`.

Easy User-land CSPRNG

Поддержка массивов в объявлениях define


В php 5.6 появилась возможность испльзовать массивы в константах (const),
теперь эта возможность добралась и до define.

define('ALLOWED_IMAGE_EXTENSIONS', ['jpg', 'jpeg', 'gif', 'png']);


Доработка Reflection



В PHP7 добавились два новых класса рефлексии.

Первый `ReflectionGenerator`, используется для интроспекции генераторов:

class ReflectionGenerator
{
    public __construct(Generator $gen)
    public array getTrace($options = DEBUG_BACKTRACE_PROVIDE_OBJECT)
    public int getExecutingLine(void)
    public string getExecutingFile(void)
    public ReflectionFunctionAbstract getFunction(void)
    public Object getThis(void)
    public Generator getExecutingGenerator(void)
}


Второй `ReflectionType` для лучшей работы со скалярными и возвращяемыми типами:

class ReflectionType
{
    public bool allowsNull(void)
    public bool isBuiltin(void)
    public string __toString(void)
}


Также, класс `ReflectionParameter` обзавелся двумя новыми методами:

class ReflectionParameter
{
    // ...
    public bool hasType(void)
    public ReflectionType getType(void)
}


Новые методы в `ReflectionFunctionAbstract`:

class ReflectionFunctionAbstract
{
    // ...
    public bool hasReturnType(void)
    public ReflectionType getReturnType(void)
}


Необратимые изменения
Зарезервированные имена `ReflectionGenerator`, `ReflectionType`.

Изменения с PHP 5.x



Новые правила использования зарезервированных ключевых слов


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

Т.е. если раньше название метода зарезерварованным словом (new/for/etc...) приводило к ошибке, теперь это разрешено:

Project::new('Project Name')->private()->for('purpose here')->with('username here');


Единственным ограничением остается слово `class`, оно не может быть использовано в названии константы,
иначе возникает конфликт с `ClassName::class`.

Context Sensitive Lexer

Унифицированный синтаксис переменных


Одно из основных изменений!
Теперь обращения к сложносочиненным переменным разбираются последовательно СЛЕВА НАПРАВО.

// вложенность ::
$foo::$bar::$baz // доступ к свойству $baz свойства $foo::$bar
 
// вложенность ()
foo()() // вызываем() результат вызова foo()
 
// операторы над выражениями заключенными в ()
(function () {})() // IIFE синтаксис JS


Новый разбор вложенности переменных более интуитивен

// old meaning            // new meaning
$$foo['bar']['baz']     ${$foo['bar']['baz']}     ($$foo)['bar']['baz']
$foo->$bar['baz']       $foo->{$bar['baz']}       ($foo->$bar)['baz']
$foo->$bar['baz']()     $foo->{$bar['baz']}()     ($foo->$bar)['baz']()
Foo::$bar['baz']()      Foo::{$bar['baz']}()      (Foo::$bar)['baz']()


Необратимые изменения
Старый код написанный с использованием {} для обработки переменных возможно не будет работать в новой версии PHP7.

Uniform Variable Syntax

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


Ранее движок PHP не позволял перехватывать фатальные ошибки, теперь эти ошибки преобразованы в исключения,
которые пользователь может обработать. И что не менее важно, конструкции finally и деструкторы объектов будут гарантированно вызваны в случае фатальных ошибок.
Дополнительным преимуществом новой системы обработки ошибок являтеся возможность получить стек вызовов (stack traces) во время ошибки.

function sum(float ...$numbers) : float
{
    return array_sum($numbers);
}
 
try {
    $total = sum(3, 4, null);
} catch (TypeError $typeErr) {
    // ошибки несоответствия типа
}


Иерархия исключений

interface Throwable
    |- Exception implements Throwable
        |- ...
    |- Error implements Throwable
        |- TypeError extends Error
        |- ParseError extends Error
        |- AssertionError extends Error
        |- ArithmeticError extends Error
            |- DivisionByZeroError extends ArithmeticError


Необратимые изменения
- Кастомные обработчики ошибок для recoverable fatal ошибок больше не будут работать
- Ошибки разбора в `eval()`-коде теперь генерируют исключения, которые нужно ловить в `try...catch`

Exceptions in the Engine

Интерфейс Throwable


Теперь класс Exception следует контракту Throwable,
эта унификация нужна для работы выражений catch-all (`catch (Exception $e)`) в легаси-скриптах, когда фатальные ошибки не являлись исключениями.

interface Throwable
    |- Exception implements Throwable
        |- ...
    |- Error implements Throwable
        |- TypeError extends Error
        |- ParseError extends Error
        |- AssertionError extends Error
        |- ArithmeticError extends Error
            |- DivisionByZeroError extends ArithmeticError


`Exception` и `Error` следуют интерфейсу `Throwable`

interface Throwable
{
    final public string getMessage ( void )
    final public mixed getCode ( void )
    final public string getFile ( void )
    final public int getLine ( void )
    final public array getTrace ( void )
    final public string getTraceAsString ( void )
    public string __toString ( void )
}


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

Throwable Interface

Целочисленная семантика


Поменялась семантика работы с целыми чилами для обеспечения кроссплатформенности:
- Кастинг `NAN` и `INF` в integer всегда = 0
- Битовый сдвиг `<<, >>` на отрицательное число теперь запрещена (возврат false, с ошибкой E_WARNING)
- << левый сдвиг с количством бит превышающем размер целого всегда = 0
- >> правый сдвиг с количством бит превышающем размер целого = 0 или -1 (зависит от знака)

Необратимые изменения
Код полагающийся на старые семантические правила не будет работать

Integer Semantics

Расширение JSON Extension заменено на JSOND


Из-за проблем с лицензированием старого расширения JSON, которое создавало проблемы для линуксойдов, решено заменить расширение на JSOND.
В новой библиотеке присутствует выигрыш в производительности и, что печально, обратно несовместимые изменения.

Необратимые изменения
- Число не должно заканчиваться точкой "." (`34.` должно быть `34.0` или `34`)
- Экспонента `e` не должна следовать сразу за точкой (`3.e3` должно быть `3.0e3` или просто `3e3`)

Replace current json extension with jsond

ZPP ошибка при переполнении

ZPP: zend_parse_parameters
При передаче float параметра в функцию, которая ожидает integer, может произойти обрезание параметра до размеров integer. Это может повлечь за собой трудно выявляемые баги. Поэтому при ошибочном конвертировании float в integer будет возвращен null и сгенерирована ошибка E_WARNING.

Необратимые изменения
Старый код, который раньше работал теперь будет генерировать E_WARNING и может перестать работать, если
результат вызова функции напрямую передается в другую функцию (т.к. будет передаваться `null`).

ZPP Failure on Overflow

Доработки foreach()


Цикл foreach имеет несколько неприятных особенностей когда работает с копиями и ссылками на массив;
нерабочие манипуляторы указателя массива current(), reset(); изменение массива, по которому проходит итерация..

Подробнее про foreach читать тут

Fix "foreach" behavior

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


В документации list() сказано, что оператор не поддерживает строки, одно рассмотрим ситуацию

// разыменовывание массива
$str[0] = 'ab';
list($a, $b) = $str[0];
echo $a; // a
echo $b; // b
 
// разыменовывание объекта
$obj = new StdClass();
$obj->prop = 'ab';
list($a, $b) = $obj->prop;
echo $a; // a
echo $b; // b
 
// возврат функции
function func()
{
    return 'ab';
}
 
list($a, $b) = func();
var_dump($a, $b);
echo $a; // a
echo $b; // b


Теперь такую возможность закрыли и list() со строками запрещен совсем.
Также, вызов list() с пустым аргументом вызовет фатальную ошибку;
порядок присвоения переменных изменился с лево-направо:

$a = [1, 2];
list($a, $b) = $a;
 
// БЫЛО:  $a = 1, $b = 2
// СТАЛО: $a = 1, $b = null + "Undefined index 1"
 
$b = [1, 2];
list($a, $b) = $b;
 
// БЫЛО:  $a = null + "Undefined index 0", $b = 2
// СТАЛО: $a = 1, $b = 2


Необратимые изменения
list() не распаковывает строки!
Вызов list() без параметров ведет к ошибке
Изменен порядок распаковки, в некоторых случаях может возвращаться null

Fix list() behavior inconsistency[/off]

Изменения в семантике деления на 0


До PHP7, когда делитель был 0 для операторов деления / или модуля %, возвращалась ложь (false) и генерировалось предупреждение E_WARNING.
Это явный бред для математической операции возвращать булево значение, поэтому такое поведение поправили.

Теперь оператор деления возвращает float равный INF, -INF, или NAN.
Оператор остатка больше не выдает E_WARNING, а бросает исключение DivisionByZeroError (intdiv() делает также).
Кстати, intdiv() также выкидывает исключение ArithmeticError, когда в результате слечается переполнение integer.

var_dump(3/0); // float(INF) + E_WARNING
var_dump(0/0); // float(NAN) + E_WARNING
 
var_dump(0%0); // DivisionByZeroError
 
intdiv(PHP_INT_MIN, -1); // ArithmeticError


Необратимые изменения
Оператор деления больше не возвращает `false`
Оператор остатка от деления (модуль) бросает исключение при делении на 0 вместо возврата false

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


При реализации своего обработчика сессий, функции `SessionHandlerInterface` которые ждут возврата `true` или `false` не работают как положено.
Ошибка приводила к тому, что только -1 трактовалась как false, а реальный false выдавался за истину..

class FileSessionHandler implements SessionHandlerInterface
{
    private $savePath;
 
    function open($savePath, $sessionName)
    {
        return false; // always fail
    }
 
    function close(){return true;}
 
    function read($id){}
 
    function write($id, $data){}
 
    function destroy($id){}
 
    function gc($maxlifetime){}
}
 
session_set_save_handler(new FileSessionHandler());
 
session_start(); // до PHP 7 ошибка не возникала


Теперь этот код упадет с фатальной ошибкой.

Необратимые изменения
- boolean false воспринимается как false. false, -1 вызовут фатальную ошибку
- Любое другое значение помимо boolean, `0`, или `-1` будет неудачно и вызовет предупреждение

Fix handling of custom session handler return values[/off]

Конструкторы в стиле PHP4 устарели


Конструкторы вида className::className() больше не используются, остался только __construct().

Удалено предупреждение date.timezone


Если не установлена временная зона date.timezone в файле конфигурации, php выдавал предупреждение. Теперь зона по-умолчанию принимается за UTC и ошибки не возникает.

Альтернативные PHP тэги удалены


Раритеты <% (<%=), %>, <script language="php">, и </script> окончательно выпилены

Множественный default блок в Switch-e


Раньше можно было указать несколько меток default в операторе switch (исполнялась только последняя),
Теперь данное поведение вызовет фатальную ошибку.

Одинаковые названия аргументов фукнции


Фатальную ошибку вызовет использование в фукнции аргументов и одинаковыми названиями.

function foo($version, $version)
{
    return $version;
}
 
echo foo(5, 7);
 
// до PHP 7
7
 
// PHP 7+
Fatal error: Redefinition of parameter $version in /redefinition-of-parameters.php


Удаленные SAPI


Следующие модули удалены из ядра (доступны в PECL):
- sapi/aolserver
- sapi/apache
- sapi/apache_hooks
- sapi/apache2filter
- sapi/caudium
- sapi/continuity
- sapi/isapi
- sapi/milter
- sapi/nsapi
- sapi/phttpd
- sapi/pi3web
- sapi/roxen
- sapi/thttpd
- sapi/tux
- sapi/webjames
- ext/mssql
- ext/mysql
- ext/sybase_ct
- ext/ereg

Removal of dead or not yet PHP7 ported SAPIs and extensions

HEX-числа в строках


Удалена поддержка преобразований строк содержащих шестнадцатеричные числа.

var_dump(is_numeric('0x123'));
var_dump('0x123' == '291');
echo '0x123' + '0x123';
 
// до PHP 7
bool(true)
bool(true)
582
 
// PHP 7+
bool(false)
bool(false)
0


var_dump((int) '0x123'); // int(0)


Вместо простого кастига, строковые hex-числа должны валидироваться и конвертироваться в целое число с помощью функции filter_var():

var_dump(filter_var('0x123', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX)); // int(291)


Необратимые изменения
Новое поведение поломает функцию is_numeric() и операторы `==`, `+`, `-`, `*`, `/`, `%`, `**`, `++`, and `--`

Remove hex support in numeric strings

Удаление старого функционала


- mysql extension (ext/mysql)
- ereg extension (ext/ereg)
- присвоение &new по ссылке
- вызов нестатических методов из вне Class::method(), где method() - нестатический метод

Необратимые изменения
Код с предупреждениями об устаревании (deprecation warning) в PHP 5 больше не будет работать (предупреждали же!)

Remove deprecated functionality in PHP 7

Переклассифицирование и удаление сообщений E_STRICT


Предупреждения категории E_STRICT удалены из PHP, или переквалифицированны в E_DEPRECATED / E_NOTICE / E_WARNING.

Необратимые изменения
Так как E_STRICT низший уровень ошибок, любое повышение ошибки до E_WARNING может поломать кастомные обработчики ошибок

Reclassify E_STRICT notices

Устаревание опции Salt функции password_hash()


С появлением нового API хэширования паролей в PHP5.5, многие стали использовать его и генерировать свои собственные "соли". К сожалению программисты зачастую используют криптографически не безопасные функции типа mt_rand(), делая свою "соль" слишком слабой, по отношению к сгенерированной по-умолчанию. В общем, пользователям запретили использовать несекьюрные соли.

Ошибки восьмеричных литералов


Неправильные восьмеричные литералы теперь генерируют ошибку парсинга, а не проглатываются как раньше.

echo 0678; // Parse error:  Invalid numeric literal in...


substr() изменение возвращаемого значения



substr() теперь возвращает пустую строку вместо false, когда начальная позиция подстроки совпадает с длиной строки.

var_dump(substr('a', 1));
 
// до PHP 7
bool(false)
 
// PHP 7+
string(0) ""


В остальных случаях substr() может попрежнему возвращать false

Что там с PHP6


Он утонул. В PHP6 планировали внедрить полную поддержку юникода в ядро, но эта затея оказалась слишком неподъемной, объем работ слишком велик.
Когда ребята это поняли, решено было пропустить мажорную версию (6), чтобы PHP не ассоциировался с фейлом, так началась работа над PHP7,
в который вошли многие наработки нерожденного.

Name of Next Release of PHP


PHP7 Что нового - первая часть

@Ссылки
Оригинал статьи
Миграция с PHP5.X -> PHP7
Онлайн выполнение PHP кода для тестирования всех версий PHP