Миграция с PHP5 на PHP7 и PHP8: что чаще всего приходится править в исходниках
Если не считать необходимого теперь перехода с MySQL на MySQLi и борьбы с Warning: A non-numeric value encountered, есть ещё несколько типовых проблем при миграции скриптов с PHP 5.X (особенно если исходная версия ниже 5.3) на PHP 7.3 - 7.4, а позднее и на PHP 8. Попробую приспособить для описания решений эту заметку, если ещё всплывёт подобное.
Для проверки фрагментов кода предполагалось, что в его начале указана директива
error_reporting (E_ALL);
Уведомление Trying to access array offset on value of type null/bool/int
Часто возникает при использовании синтаксиса обращения к элементу массива на других типах данных. Пример:
$a = false; var_dump($a['somekey']); // PHP 7.3: // NULL // // PHP 7.4: // Notice: Trying to access array offset on value of type bool in Command line code on line ...
Так бывает, например, если функция может возвращать массив в нормальном случае и false/null в случае ошибки, а дальше вверх по стеку информация о false/null теряется и этот случай не обрабатывается отдельно.
Решение - проверять с помощью is_array, является ли объект массивом.
Применение функции mysql_real_escape_string
...которой традиционно "обезопасивали" хранимые в базе данных строки от SQL-инъекций.
было: mysql_real_escape_string($s)
надо: mysqli_real_escape_string ($id,$s)
, где $id
- идентификатор соединения MySQLi. Или хотя бы addslashes($s)
- если нужно только экранировать "опасные" для SQL-запроса кавычки.
Перебор массива с помощью list и each
было: while (list(,$var) = each($params)) {
надо: foreach ($params as $var) {
или же: while (list($num,$var) = each($params)) {
а теперь: foreach ($params as $num=>$var) {
- если массив перебирается как ассоциативный и/или нужны ключи его элементов.
Модификатор /e функции preg_replace
было:
$text = preg_replace('~([0-9]+)\^([0-9]+)~e', 'pow("\\1", "\\2")', $text); //вычислить a^b
надо:
$text = preg_replace_callback('~([0-9]+)\^([0-9]+)~',
function ($m) { return pow($m[1], $m[2]); }, $text); //вычислить a^b
- то есть, через callback-функцию.
Проблема с подключаемыми графическими шрифтами GD
было:
$bbox=imagettfbbox ($f,0,'arial.ttf','String');
надо:
$font = dirname(__FILE__) . '/arial.ttf';
$bbox=imagettfbbox ($f,0,$font,'String');
- если фонт лежит в папке скрипта, как обычно и бывает.
То же самое с imageTtfText
, например:
$font = dirname(__FILE__) . '/Roboto-Bold.ttf'; imageTtfText($myimg, $size, $angle, $j, 30, $c, $font, $z);
error_reporting(0) и подобное
Многие разработчики привыкли решать проблему с предупреждениями и даже сообщениями об ошибках просто выключая сообщения о них. При миграции скриптов это приводит к "загадочным белым экранам" вместо содержимого. Лучше всего вообще не трогать включённое в новых версиях протоколирование ошибок по умолчанию, а все вызовы функции error_reporting
приводить к виду, указанному в начале статьи.
Строковые функции
Начиная с версии PHP 5.6 кодировкой по умолчанию стала кодировка Юникода UTF-8, соответственно, вместо прежних "си-подобных" строковых функций теперь лучше использовать их многобайтовые аналоги.
Наиболее часто приходится менять:
- вместо
strlen($s)
писатьmb_strlen($s,'UTF-8')
; - вместо
strpos ($haystack,$needle,0)
писатьmb_strpos ($haystack,$needle,0,'UTF-8')
; - вместо
strstr ($haystack,$needle,false)
писатьmb_strstr ($haystack,$needle,false,'UTF-8')
; - вместо
substr ($string,$start,$length)
писатьmb_substr ($string,$start,$length,'UTF-8')
...и т.д., принцип, думаю, понятен. Будьте внимательны, проверяя, есть ли для функции многобайтовый аналог.
Для "бинарно безопасных" функций strcmp
/ strcasecmp
, к примеру, таковых нет, а сравнение всё равно не работает:
<?php $s1="Привет"; $s2="привет"; echo strcasecmp($s1,$s2); //-32 ?>
и нужно делать так:
<?php function mb_strcasecmp($str1, $str2, $encoding = null) { if (null === $encoding) { $encoding = mb_internal_encoding(); } return strcmp(mb_strtoupper($str1, $encoding), mb_strtoupper($str2, $encoding)); } $s1="Привет"; $s2="привет"; echo mb_strcasecmp($s1,$s2,'UTF-8'); //0 ?>
С другой стороны, как видно из примера, для strtoupper
и strtolower
эти аналоги есть.
Отдельно нужно учесть изменения с htmlspecialchars.
Больше о работе со строками UTF-8 в PHP 7/8 в этой статье блога.
Применение array_key_exists к объекту, а не к массиву
Теперь нельзя применять array_key_exists
к объектам классов (а можно только к массивам). Неправильно:
class foo { public $a, $b; }; $bar = new foo(); echo (array_key_exists('a',$bar) ? 'yes' : 'no'); //deprecated
Правильно:
echo (property_exists($bar,'a') ? 'yes' : 'no'); //yes
Итак, 8-я версия, вышедшая в ноябре-2020, теперь есть и в составе XAMPP, ниже, вероятнее всего, будут добавляться исправления для PHP8, хотя и для версии 7 всё написанное выше остаётся в силе.
Отмечу, что в сборке PHP 8.0.0 с сайта XAMPP в файле
php.ini
(диск:\xampp\php\php.ini
) была по умолчанию отключена библиотека gd:;extension=gdСоответственно, все функции imagecreatetruecolor, imagecreatefromgif, imagecreatefromjpeg, imagecreatefrompng и т.д. "вдруг перестали работать".
Решение - убрать точку с запятой из первой позиции указанной выше строки, сохранить изменённый
php.ini
, перезагрузиться.
Функция get_magic_quotes_gpc удалена (PHP8)
Любые упоминания о функции get_magic_quotes_gpc теперь придётся исключить из кода, она просто удалена. При этом нужно учесть, что начиная с версии 5.4 она всё равно всегда возвращала 0.
Почему нельзя было оставить в реализации пустое имя функции, а надо заставлять людей переделывать кучу кода - совершенно неясно.
И так у них всё :(
Функция each удалена (PHP8)
Удалена в PHP8 и функция each. В общем случае можно попытаться заменить на next или array_shift, но однозначного рецепта нет из-за возможности использования функции не только в цикле, как показано выше, но и, например, в рекурсивных построениях.
Фигурные скобки и литералы без кавычек для индексов массива (PHP8)
В PHP8 больше нельзя писать $a{$i}
вместо $a[$i]
или $foo[bar]
вместо $foo['bar']
. Просто исправьте это.
Разумеется, если ключ является числовой константой или берётся из переменной, заключать его в апострофы не нужно, $i = 1; $a[$i]
или $a[1]
- это верный синтаксис.
Потеря точности при извлечении целого числа (PHP8)
PHP 8 плохо относится к отсутствию явного преобразования типа при извлечении числа и ругается "Deprecated: Implicit conversion from float (число) to int loses precision". Общее правило - (int)(a)
или intval(a)
, например:
<?php error_reporting(E_ALL); $number = 1100; $res = log10(abs($number)) / 3 | 0; //Предупреждение Implicit conversion from float echo $res; $res = intval(log10(abs($number)) / 3); //ОК echo '<br>'.$res; $res = (int)(log10(abs($number)) / 3); //ОК echo '<br>'.$res; ?>
Проверка наличия элемента в массиве с помощью ! или неявного условия (PHP8)
Больше нельзя проверять наличие элемента массива кодом вида
if (!$arr[$index])
, а только
if (!isset($arr[$index]))
Аналогично, не
if ($arr[$index])
а
if (isset($arr[$index]))
Сравнение идентификатора соединения с нулём (PHP8)
PHP 8 не разрешает сравнивать идентификатор соединения с нулём.
$conid = mysqli_connect("host", "user", "password"); //хост, логин, пароль if ($conid == 0) //Ошибка!
но if (!$conid)
или if ($conid!==false)
- нормально.
13.02.2020, 17:35 [24803 просмотра]