Переход на PHP7: работа над ошибками

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

Если сайт уже давно не молод, то с большой долей вероятности чудо не произойдет. В лучшем случае начнут появляться разного рода ошибки, а в худшем  — вы увидите белый экран, дзен веб-разработки. В этот момент хочется по-быстрому переключить все обратно и забыть о своей внезапной слабости.

Но предположим, что ваша сильная сторона — настойчивость, к тому же вы располагаете некоторым количеством времени для экспериментов. Давайте попробуем все починить.

PHP7_sprinthost

Резервные копии

Делаем резервные копии сайта (а заодно и баз данных). Ведь кто не делает резервные копи — сам себе враг, верно? Для разного рода экспериментов имеет смысл добавить еще один сайт на хостинге и скопировать в него файлы, которые мы сейчас будем править.

Журналы ошибок

Настроим ведение журнала ошибок PHP в файл .htaccess (если он не был настроен ранее):

php_value display_errors 0
php_value log_errors 1
php_value error_log /home/vasya/domains/mysite.ru/logs/error.log

Подробно о ведении журналов мы уже писали в нашем блоге ранее.

Работа с MySQL

Допустим, сайт использует базы данных, и вы видите ошибки вроде такой:

Fatal error: Uncaught Error: Call to undefined function mysql_connect()

Это оттого, что в современных версиях PHP (начиная с PHP 5.5.0) оригинальное расширение MySQL не поддерживается. Разработчики рекомендуют использовать MySQLi или PDO. Попробуем перейти на MySQLi, это просто:

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

$link = mysql_connect(‘localhost’, $user, $password)
mysql_select_db($dbname, $link)
mysql_query(‘set names cp1251’)

можно заменить на:

$link = mysqli_connect(‘localhost’, $user, $password, $dbname)
mysqli_query($link, ‘set names cp1251’)

для запросов:

$result=mysql_query($query,$cid)

заменить на:

$result=mysqli_query($cid, $query)

Другие популярные функции легко меняются на их аналоги с буквой ‘i’:

mysqli_fetch_array()
mysqli_fetch_row()
mysqli_fetch_assoc()
mysqli_fetch_array()
mysqli_num_rows()
mysqli_insert_id()
mysqli_close()

В результате этих несложных действий данные из БД должны успешно собираться и отправляться.

Кодировка

Настоящий олдскул — это сайт в CP1251 (как минимум). Всё превратилось в  ромбики или прочие козяблики?

incorrect

Скорее всего, достаточно будет указать кодировку в .htaccess таким образом:

php_value default_charset «cp1251»

Регулярные выражения

Также вы можете наблюдать ошибки следующего рода:

Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead

Это означает, что модификатор /e, который позволял передать произвольной функции результат регулярного выражения, теперь не поддерживается. В таких случаях рекомендуется использовать функцию preg_replace_callback

Допустим, у нас есть такое регулярное выражение:

$string=preg_replace(«/:([a-z]{1,10}):/e», «print_smile(‘\\1’)», $string)

с заменой на preg_replace_callback оно должно выглядеть вот так:

$string=preg_replace_callback(«/:([a-z]{1,10}):/», create_function(‘$matches’, ‘return print_smile($matches[1])’), $string)

здесь все просто, регулярное выражение теперь указывается в качестве первого аргумента (без модификатора /e, разумеется), а в качестве второго аргумента указывается анонимная функция (которая будет выполнена после применения регулярного выражения) с двумя аргументами: массив $matches, где будут сохранены данные, совпадающие с регулярным выражением и вызов внешней функции с аргументами. В данном примере внешняя функция называется print_smile и ей передается аргументом первое найденное вхождение. То, что в preg_replace было \\1 (первое найденное вхождение) станет $matches[1] (если аргументов было больше, то будет $matches[2], $matches[3] и так далее).

Вот еще один пример, посложнее:

Было так:

$out=preg_replace(«/<[Rr][Mm]( [Mm][Ss][Gg]=[\’\»]{0,1}|)(.*?)([\’\»]{0,1})>(.*?)<\/[Rr][Mm]>/es», «feed_out_sub_rm(‘\\2′,’$base_prefix’,’$nick’,’$id_entry’)», $out)

Стало вот так:

$out=preg_replace_callback(‘/<[Rr][Mm]( [Mm][Ss][Gg]=[\’\»]{0,1}|)(.*?)([\’\»]{0,1})>(.*?)<\/[Rr][Mm]>/s’, create_function(‘$matches’, ‘return feed_out_sub_rm($matches[2], «‘.$base_prefix.'», «‘.$nick.'», «‘.$id_entry.'»)    ‘), $out)

здесь легко запутаться в кавычках, будьте внимательны.

Копаясь в регулярных выражениях, можно вспомнить еще про две функции, которые с версии PHP 5.3.0 считаются устаревшими (и не поддерживаются). Симптомы следующие:

Fatal error: Uncaught Error: Call to undefined function ereg_replace()

Если регулярное выражение в ereg_replace простое, то можно обойтись просто установкой граничных символов, как здесь:

$str=ereg_replace(«[\r\t\n]»,»»,$str)
$str=preg_replace(«/[\r\t\n]/»,»»,$str)

Аналогичный симптом:

Fatal error: Uncaught Error: Call to undefined function split()

Было:

$var_pair=split(«=»,$tmp[3])

Стало:

$var_pair=explode(«=»,$tmp[3])

Если регулярное выражение посложнее, то пробуем преобразовать к preg_split.

На этом пока все. Удачной отладки.
Много полезных материалов по теме можно найти на сайте разработчиков.

Если что-то не получается, или ваш случай совсем не похож на наши примеры — пишите комментарии, попробуем разобраться вместе.

Для того, чтобы оставлять комментарии к посту, авторизуйтесь, используя свой аккаунт в социальных сетях ВКонтакте/FaceBook, или аккаунт в Google/Яндекс.

php7_elephant

комментария 2

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