PHP 7.3: Изменения

Опубликовано 3 комментариев 1328 просмотров
PHP 7.3: Изменения

Эта запись дополняется в live-режиме и будет дополняться до тех пор, пока не состоится публичный релиз PHP 7.3.

UPD от 7.12.18: релиз стабильной версии состоялся, о том как установить/обновить PHP до 7.3 вы можете прочитать здесь.

По плану до конца лета 2018 года будет определен полный список новшеств, которые войдут в новую версию PHP 7.3. Выпуск стабильной версии запланирован на 29 ноября.

Изменение требований к Heredoc и Nowdoc

Синтаксис Heredoc и Nowdoc, который помогал при использовании многострочных строк, имел жесткое требование - в строке, содержащей идентификатор недопустимо было использование каких-либо еще символов (кроме ;).

Например:

$foo = <<<IDENTIFIER
the crazy dog jumps over the lazy fox
"foo" bar;
IDENTIFIER

В строке с последним IDENTIFIER не могло находиться ничего, кроме IDENTIFIER - как до, так и после идентификатора (кроме исключения).

RFC для PHP 7.3 убирает это ограничение с целью сделать код более читаемым. При этом пришлось сломать отступы, используемые в остальной части кода, так что в here/now теперь могут использоваться токены.

RFC вносит изменения в синтаксис Heredoc/Nowdoc:

  1. Строка с закрывающим токеном может начинаться с отступов (пробелы и табы).
  2. Пробелы и табы не должны смешиваться. Иначе будет вызвана ошибка Parse error: Invalid indentation - tabs and spaces cannot be mixed in .. on line ...
  3. Отступы, находящиеся в закрывающей строке перед идентификатором будут удалены из получаемой переменной.
  4. Если число символов отступов, используемых в конце токена превышает число символов отступов в выражении, вы получите Parse error: Invalid body indentation level (expecting an indentation level of at least ..) in .. on line ...
  5. После закрывающего токена можно использовать другие выражения PHP.

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

$foo = ['foo', 'bar', <<<EOT
  baz
    -  hello world! --
  ahoy
  EOT, 'qux', 'quux'
];

var_dump($foo);
array(5) {         
  [0]=>            
  string(3) "foo"  
  [1]=>            
  string(3) "bar"  
  [2]=>            
  string(29) "baz  
  -  hello world! --
ahoy"
  [3]=>
  string(3) "qux"
  [4]=>
  string(4) "quux"
}

Обратите внимание как пробелы, введеные в закрывающей строке перед токеном не попали в финальный вывод var_dump(), а после закрытия токена EOT мы продолжили добавлять элементы в массив.

Обратная совместимость

$foo = <<<HELLO
  HELLO_WORLD <-- не вызовет завершение строки
  HELLOWORLD <-- тоже не вызовет
  HELLO WORLD <-- строка завершится
HELLO;

Новая версия PHP предполагает завершение на 4 строке вместо 5, как было в более ранних версиях.

Разрешение запятой в вызовах функций и методов

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

foo('bar', 'baz',); // Обратите внимание на конечную запятую после 'baz'

В ранних версиях PHP будет вызвана ошибка Parse error: syntax error, unexpected ')' in .. on line ...

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

function foo($bar, $baz, ) // по прежнему запрещено
{
}

Лично я так и не смог понять/грамотно перевести причину, по которой вообще ввели такой синтаксис. Единственное предположение - объявление массива вида ['foo' => 'bar',] не вызывает никаких ошибок, и данное нововведение имеет чисто визуальную необходимость..

Возможность вызвать исключение при работе с json_encode() и json_decode()

Все время существования json_encode() и json_decode() вместо ошибки в случае возникновения таковой возвращали false. Нововведение добавляет возможность указать JSON_THROW_ON_ERROR 4 аргументом для вызова ошибки:

try {
  json_decode("{", false, 512, JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
  echo $exception->getMessage();
}

Новый \JsonException является подклассом \Exception и, как и константа JSON_THROW_ON_ERROR, объявляются в глобальном пространстве имен.

На данный момент имеются библиотеки вроде daverandom/exceptional-json, добавляющие аналогичный функционал в PHP 7.2. С релизом PHP 7.3 вы можете избавиться от лишних пакетов и вызовов json_last_error() каждый раз после выполнения операции.

Обратная соместимость

Если вы объявили свое собственное исключение и/или константу с аналогичными именами - пришло время их изменить.

Ссылки в list()

list() полезен для быстрой установки значений переменных из массива. До PHP 7.3 невозможно было присвоить переменные по ссылке и следующий фрагмент вызывал фатальную ошибку:

$arr = ['apple', 'orange'];
list($a, &$b) = $arr;
$b = 'banana';
echo $arr[1];
// Fatal error: [] and list() assignments cannot be by reference in .. on line ..

В PHP 7.3 echo $arr[1] будет выводить banana. Синтаксис [$a, &$b] = $arr также получит это нововведение.

Добавлена функция is_countable()

PHP 7.2 вызывал предупреждение при вызове count() с не-счетным (countable) аргументом. По сути, is_countable() является логичной альтернативой $foo instanseof \Countable, которую почему-то забыли добавить в PHP 7.2. До официального релиза можно воспользоваться моим вариантом:

function is_countable($arg)
{
    return is_array($arg) || $arg instanceof \Countable;
}

Объявлена устаревшей функция image2wbmp()

Функция image2wbmp(), начиная с PHP 5.x являющейся фактически идентичной imagewbmp(), объявлена устаревшей как бессмысленный дубль второй и будет удалена в следующей версии (предоположительно, PHP 8.x).

Миграция на PCRE2

PCRE является ядром для функций, связанных с регулярными выражениями. Сегодня в PHP используется версия PCRE 8.x, которая уже несколько лет является устаревшей. Основная цель нововведения - добавить в PHP 7.3 актуальный стабильный релиз PCRE2, максимально сохранив совместимость.

Вызов уведомления при использовании не определенной переменной в compact()

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

$foo = 'bar';

$baz = compact('foz'); // Notice: compact(): Undefined variable: foz

Улучшения хеширования пароля Argon2

Это нововвeдение предназначено для улучшение функционала, введенного в Argon2 Password Hash.

Argon2 имеет три варианта хеширования: Argon2i, Argon2d и Argon2id. Argon2d работает быстрее и использует зависящий от данных доступ к памяти, что делает его очень устойчивым к атакам с крекингами GPU и подходит для приложений без угроз от атак со сторонних каналов (например, криптовалюты). Argon2i вместо этого использует независимый от данных доступ к памяти, который предпочтительнее для хэширования паролей но медленнее, поскольку для защиты от атак делает больше проходов по памяти. Argon2id является гибридом Argon2i и Argon2d, и использует комбинацию доступа к данным, зависящим и не зависящим от данных, что дает некоторую устойчивость Argon2i к атакам на кеш-сервер и большую часть устойчивости Argon2d к атакам крекинга GPU.

Нововведение добавляет поддержку алгоритма хеширования Argon2id в функции password_*.

Новые константы

Добавлена новая константа PASSWORD_ARGON2ID.

Изменения password_hash()

Функция password_hash() может работать с PASSWORD_ARGON2ID:

// Argon2id с опциями по-умолчанию
password_hash('password', PASSWORD_ARGON2ID);

Реализация будет действовать идентично Argon2i и принимать идентичные опции:

// Argon2id ведет себя так же, как Argon2i
password_hash('password', PASSWORD_ARGON2ID, ['memory_cost' => 1<<17, 'time_cost' => 4, 'threads' => 2]);

Argon2id будет использовать те же стандартные опции, что и Argon2i.

Изменения password_verify()

Функция password_verify() совместима с Argon2id.

Изменения password_get_info()

Функция password_get_info() принимает Argon2id и возвращает информацию об алгоритме Argon2.

var_dump(password_get_info('$argon2id$v=19$m=1024,t=2,p=2$ZUhOUVczSHpZRDBDU2ZBRA$k/vI1wKP4s0ecJIpUybRfgBeo3as1PhIV1Od6PvOEFA'));

array(3) {
  ["algo"]=>
  int(3)
  ["algoName"]=>
  string(8) "argon2id"
  ["options"]=>
  array(3) {
    ["memory_cost"]=>
    int(1024)
    ["time_cost"]=>
    int(2)
    ["threads"]=>
    int(2)
  }
}

Изменения password_needs_rehash()

Функция password_needs_rehash() принимает хеши Argon2id.

$hash = password_hash('password', PASSWORD_ARGON2ID);
password_needs_rehash($hash, PASSWORD_ARGON2ID); // false
password_needs_rehash($hash, PASSWORD_ARGON2ID, ['memory_cost' => 1<<17]); // true

Регистронезависимые константы объявлены устаревшими

Сегодня константы разделены по их типу на 2 категории: регистрозависимые и регистронезависимые. PHP 7.3 отказывается от последнего типа и вводит следующие изменения:

  • Вызов define с третьим параметром true будет вызывать ошибку
  • Запрет вызова константы с именем, отличным от указанного при ее объявлении. Исключение - true, false и null
define('FOO', 42, true); // Deprecated: define(): Declaration of case-insensitive constants is deprecated
var_dump(FOO); // Ок
var_dump(foo); // Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO"

В PHP 8.0 будет полностью удалена возможность вызова регистронезависимых констант. true, false и null будут по прежнему доступными зарезервированными ключевыми словами.

array_key_first и array_key_last - вовращение первого/последнего ключа массива

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

Будут добавлены новые функции:

  • array_key_first - возвращает первый ключ массива
  • array_key_last - возвращает последний ключ массива

Ключевая особенность - исходный массив не будет изменен.

// ассоциативный массив
$array = ['a' => 1, 'b' => 2, 'c' => 3];

$firstKey = array_key_first($array);
$lastKey = array_key_last($array);

assert($firstKey === 'a');
assert($lastKey === 'c');

// числовой массив
$array = [1 => 'a', 2 => 'b', 3 => 'c'];

$firstKey = array_key_first($array);
$lastKey = array_key_last($array);

assert($firstKey === 1);
assert($lastKey === 3);

// пустой массив
$array = [];

$firstKey = array_key_first($array);
$lastKey = array_key_last($array);

assert($firstKey === null);
assert($lastKey === null);

Устаревшее в PHP 7.3

Это объемный RFC с несколькими предложениями, объявляющими разный функционал устаревшим и удаляющий его (функционал) в PHP 8.x.

Незадокументированные алиасы функций mbstring

Функции mbregex_encodingmberegmberegimbereg_replacemberegi_replacembsplitmbereg_matchmbereg_searchmbereg_search_posmbereg_search_regsmbereg_search_initmbereg_search_getregsmbereg_search_getpos и mbereg_search_setpos являются незадокументированными псевдонимами аналогичных функций с префиксом mb_. (например mb_ereg).

Объявлены устаревшими, будут удалены позднее в PHP 8.x.

Функции поиска строк с целом числом в агрументах

Изменение касается следующих функций: strposstrrposstriposstrriposstrstrstrchrstrrchr и stristr.

Функции в качестве needle ожидают строку. Однако если аргумент не является строкой, он приводится к целому числу и трактуется как код символа ASCII.

$str = "There are 10 apples";
var_dump(strpos($str, "10")); // int(10)
var_dump(strpos($str, 10));   // bool(false)

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

В PHP 7.3 будет вызываться предупреждение об устаревании если в needle передан не-строковой параметр. В PHP 8.x предупреждение будет удалено, а параметр needle будет изменен на строку.

Функция fgetss и фильтр string.strip_tags

Из-за не востребованной необходимости в предоставлении аналога strip_tags для дескрипторов файлов а так же вынужденной меры из-за этого делать реализацию функции более сложной, в PHP 7.3 объявлены устаревшими fgetss()gzgetss() и SplFileObject::fgetss(). Уведомление будет вызываться при каждом вызове функции и при создании фильтра потока string.strip_tags.

Фильтр и функция будут удалены в PHP 8.

FILTER_FLAG_SCHEME_REQUIRED и FILTER_FLAG_HOST_REQUIRED

Объявлены устаревшими константы FILTER_FLAG_SCHEME_REQUIRED и FILTER_FLAG_HOST_REQUIRED (бесполезные в виду наличия FILTER_VALIDATE_URL). Будут удалены в PHP 8.

Директива в php.ini pdo_odbc.db2_instance_name

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

В функциях setcookie, setrawcookie и session_set_cookie_params параметры, используемые в заголовке Set-Cookie, - path, domain, secure и httponly - перемещены в аргумент $options. При этом текущий вариант реализации, где каждый параметр является отдельным аргументом, остается работоспособным.

bool setcookie ( string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false]]]]]] );
bool setcookie ( string $name [, string $value = "" [, int $expire = 0 [, array $options ]]] );

bool setrawcookie ( string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false]]]]]] );
bool setrawcookie ( string $name [, string $value = "" [, int $expire = 0 [, array $options ]]] );

void session_set_cookie_params ( int $lifetime [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]] );
void session_set_cookie_params ( int $lifetime [, array $options ] );

По-умолчанию $options равен пустой строке. Стандартные значения для этих параметров остаются без изменений.

В ответ на сообщение

Доступна разметка Markdown. А еще вы можете использовать крутой пак эмоций.

Нажимая на кнопку «Отправить» вы даете свое согласие на обработку персональных данных в соответствии с законом №152-ФЗ «О персональных данных» от 27.07.2006 и принимаете условия Политики конфеденциальности.