Вечером 21 октября 2026 года мой телефон разорвался от звонков. В рабочем чате, где обычно обсуждают котов и сроки сдачи макетов, висел одинокий, но очень жирный алерт от моего сканера безопасности. Который гласил, что нам всем конец.
Две критические уязвимости в PHP, раскрытые утром этого злополучного дня. Точных деталей в паблике еще небыло. Говорилось, что вендор уже выкатил фикс в 8.5.12, а все, что ниже — официально в красной зоне. Такая информация была на момент осознания масштаба проблемы.
У меня на поддержке около сотни сайтов. Разброс по серверам — от убитого shared-хостинга, где админ спит еще с нулевых, до пары десятков VPS. Версии PHP — от гордого 8.3 на свежих проектах до музейных экспонатов.
Первая мысль была рациональной: «Завтра с утра спокойно пропишем обновление в CI/CD, прогоняем тесты, выкатываем». Вторая мысль, более честная, пришла через секунду: «если дыры опубликовали сегодня, эксплойты появятся в паблике завтра к обеду». Спать я в ту ночь лег в 04:30.
Что за уязвимости и почему именно 8.5.12
Деталей CVE на момент алерта не было. Точнее не было у меня. Было другое: PHP Group четко написала в релизе — апдейт критический, рекомендуется немедленное обновление.
В 2026 году PHP 8.5 — это уже вполне рабочий мейнстрим, но в продакшене он стоит далеко не везде. Многие клиенты сидят на 8.2 или 8.3, потому что «работает — не трогай». А у меня в заначке были те самые 32 сайта, которые до сих пор дышали на ладан под PHP 5.4 и 5.6.
Это старые самописные CRM, интернет-магазины на Joomla 1.5 и форумы на vBulletin, которые клиенты отказываются переписывать, потому что «там СТОЛЬКО всего за десять лет».
Вариант «просто обновиться до 8.5» для них означал мгновенную белую страницу. Расширение mysql_* вырезали из ядра еще в PHP 7.0. Любой вызов mysql_connect() или mysql_query() на восьмерке роняет скрипт с фаталом.
Я молчу про стрикт-типизацию и многое другое, что у 8+ под капотом.
Марафон переноса: сто сайтов за трое суток
План был простой и жестокий.
-
Shared-хостинги, где у меня нет рута — тикеты в поддержку с криками, слезами или угрозами — «срочно накатите 8.5.12». Выжили не все, два провайдера ответили, что у них в планах апгрейд на декабрь. Пришлось экстренно поднимать для них контейнеры на своих VPS и перекидывать DNS.
-
Свои VPS. Тут полегче:
apt update && apt install php8.5-cli php8.5-fpm. Но дьявол, как обычно, сидел вphp.iniиextension=.
А еще:
-
Отвалились старые
IonCube Loaderдля PHP 7.4, которые зачем-то тянули старые билды WordPress и Ктулху его сзнает чего еще. -
Сломался
mcryptна одном древнем биллинге (пришлось в три часа ночи писать враппер вокругopenssl). -
Composer на паре проектов сошел с ума от того, что в
composer.jsonжестко прибито"php": ">=5.6, <8.0". Пришлось массово резать эти строки черезsed. -
Deprecated-предупреждения от
create_function()и неявных приведений типов забили логи так, что ротация не справлялась.
Из сотни сайтов 68 поднялись на 8.5.12 после простой смены версии в FPM и легкой правки конфигов, ну и пары исправлений в самих скриптах. Это были относительно свежие WordPress, Laravel, OpenCart и самописы. Я прогонял их через grep -R "mysql_", чтобы уж убедиться окончательно. — чисто.
Оставшиеся 32 проекта при переключении на восьмерку выдавали Fatal error: Uncallable function mysql_connect() и много-много чего еще…
Почему нельзя «просто переписать»
mysql_* — это проклятие легаси. В этих 32 проектах функции mysql_connect, mysql_query, mysql_fetch_assoc размазаны по сотням файлов. Где-то они обернуты в свои классы, где-то вызываются напрямую в процедурном лапше-коде.
Переписать это на PDO или хотя бы mysqli за неделю? Математически невозможно. В одном только проекте старой логистической компании было 400+ вхождений mysql_query. Даже если тратить на замену одной функции с тестированием две минуты (что очень оптимистично, ага), это 800 минуты чистого времени. Почти 14 часов на один проект. А их 32. И это не считая переписывания логики обработки ошибок, так как PDO по умолчанию кидает исключения, а старый код ждет, что mysql_query() просто молча вернет false.
Даунтайм на две недели, пока я буду в одно лицо разгребать этот спагетти-код, клиент не оплатит. А оставлять их на PHP 5.6, когда в сети уже крутят PoC для свежих CVE — это подписать акт о профессиональном самоубийстве.
А значит, нужно было решение, которое позволит запустить старый код на новой версии без правок самого кода.
Анатомия «таблетки»: mysql-tablet.php
Я не первый, кто столкнулся с этой проблемой, но готовые врапперы вроде mysqlnd-uh или mysqli_compat часто тянут лишнюю магию, которая на PHP 8.5 ведет себя непредсказуемо.
Мне нужен был максимально тупой, прозрачный слой. Один файл, который инклудится в auto_prepend_file в php.ini или .user.ini, ну или вообще просто в скрипт.
Я назвал его mysql-tablet.php. Идея простая: проверить, не объявлены ли функции mysql_* (вдруг хостинг чудит и оставил их для обратной совместимости), и если нет — объявить их поверх mysqli.
Вот как это работает под капотом.
1. Затыкаем исключения и определяем константы
Легаси-код 2012 года не знает про try-catch. Если mysqli при соединении словит ошибку, он по умолчанию бросит исключение. Старый if (!$link) просто не сработает, скрипт умрет некрасиво.
if (function_exists('mysqli_report') && defined('MYSQLI_REPORT_OFF')) { mysqli_report(MYSQLI_REPORT_OFF); }
Эта строка переводит mysqli в тихий режим. Теперь он ведет себя как старый mysql: возвращает false и пишет ошибку в свой внутренний лог, а не роняет скрипт.
Дальше — константы типов выборки. В коде везде написано mysql_fetch_array($res, MYSQL_ASSOC). В PHP 8.5 эти константы не определены глобально, если не включено расширение mysql. Пришлось объявить их вручную, маппя на MYSQLI_*:
if (!defined('MYSQL_ASSOC')) { define('MYSQL_ASSOC', defined('MYSQLI_ASSOC') ? MYSQLI_ASSOC : 1); }
2. Магия mysql_connect
Функция mysql_connect() исторически умеет принимать не только localhost, но и hostname:3306, :/tmp/mysql.sock и даже p:hostname для персистентных соединений. Мой слой должен это распарсить и правильно передать в mysqli_real_connect.
function mysql_connect($server, $username, $password, $new_link = false, $client_flags = 0) { try { if (!function_exists('mysqli_init')) { return false; } $host = (string) $server; $port = null; $socket = null; $persistentPrefix = ''; if (str_starts_with($host, 'p:')) { $persistentPrefix = 'p:'; $host = substr($host, 2); } if ($host === '') { $host = 'localhost'; } elseif (str_starts_with($host, ':')) { $socket = substr($host, 1); $host = 'localhost'; } elseif (str_contains($host, ':')) { [$hostname, $extra] = explode(':', $host, 2); if ($hostname !== '') { $host = $hostname; } if ($extra !== '') { if ($extra[0] === '/') { $socket = $extra; } else { $port = (int) $extra; } } } $host = $persistentPrefix . $host; $link = mysqli_init(); if (!$link instanceof mysqli) { return false; } $ok = @mysqli_real_connect( $link, $host, (string) $username, (string) $password, null, $port, $socket, (int) $client_flags ); if (!$ok) { $GLOBALS['mysql_oldstyle_connect_errno'] = function_exists('mysqli_connect_errno') ? mysqli_connect_errno() : 0; $GLOBALS['mysql_oldstyle_connect_error'] = function_exists('mysqli_connect_error') ? mysqli_connect_error() : ''; return false; } $GLOBALS['mysql_oldstyle_link'] = $link; $GLOBALS['mysql_oldstyle_connect_errno'] = 0; $GLOBALS['mysql_oldstyle_connect_error'] = ''; return $link; } catch (Throwable $e) { $GLOBALS['mysql_oldstyle_connect_errno'] = (int) $e->getCode(); $GLOBALS['mysql_oldstyle_connect_error'] = $e->getMessage(); return false; } }
Почему линк кладется в $GLOBALS['mysql_oldstyle_link']? Потому что в старом коде часто опускают параметр $link в последующих функциях, полагаясь на последнее открытое соединение. Глобал — грязно, но это единственный способ заставить процедурный код 15-летней давности работать без явной передачи хендла.
3. Fetch-функции и строгая проверка на false
Это самый тонкий момент, на котором ломается большая половина легаси. В старом mysql_fetch_assoc() при достижении конца выборки возвращался false. В mysqli_fetch_assoc() при пустом результате возвращается null.
Код вида while ($row = mysql_fetch_assoc($res)) { ... } на null работает. Но код вида if ($row === false) { /* какая-то спец-логика */ } на null падает.
Поэтому в таблетке я принудительно нормализую результат:
function mysql_fetch_assoc($res) { if (!$res instanceof mysqli_result) { return false; } $row = mysqli_fetch_assoc($res); return $row === null ? false : $row; }
То же самое сделано для mysql_fetch_row и mysql_fetch_array. Мы обманываем старый код, возвращая ему привычный false вместо null.
4. Ошибки соединения
Если mysql_connect() упал, старый код полезет в mysql_errno() и mysql_error(). Но если мы не сохранили ошибку в глобальные переменные, эти функции вернут пустоту.
if (!$ok) { $GLOBALS['mysql_oldstyle_connect_errno'] = function_exists('mysqli_connect_errno') ? mysqli_connect_errno() : 0; $GLOBALS['mysql_oldstyle_connect_error'] = function_exists('mysqli_connect_error') ? mysqli_connect_error() : ''; return false; }
А сами функции mysql_errno() и mysql_error() теперь просто читают эти глобалы, если им не передали конкретный линк.
Грабли и честные ограничения
Сразу предупреждаю: mysql-tablet.php — это не панацея. Это морфий, который позволяет дотащить раненого до операционной, а не вырастить ему новую ногу.
Обратить внимание: Если в коде открыто два соединения через
mysql_connect()и функции вызываются без явного указания линка, таблетка будет работать с последним открытым. В 95% случаев легаси использует один линк, но я уже словил пару экзотических систем, где это ломалось.
Еще нужно обратить внимание на:
-
Поведение ошибок. Мы выключили исключения в
mysqli, но самиmysql_*функции внутри таблетки все равно могут возвращатьfalseтам, где оригинал на PHP 5.6 мог вернуть ресурс с предупреждением. Логи FPM в первую ночь выглядели как новогодняя елка. -
Безопасность. Самое важное. Эта таблетка НЕ лечит SQL-инъекции. Если в том самом коде 2012 года переменная
$idиз$_GETбез фильтрации вставляется вmysql_query("SELECT * FROM users WHERE id = $id"), она останется уязвимой. Мы просто перенесли дыру на новый интерпретатор. -
Производительность. Микросекунды накладных расходов на враппер. На сотне сайтов это незаметно, но под высокой нагрузкой я бы десять раз подумал.
И так. Сайты живут. Уязвимости закрыты. У меня есть 6–12 месяцев относительно спокойной жизни, чтобы в каждом из этих 32 проектов выпилить mysql-tablet.php и перевести код, хотя бы, на mysqli с подготовленными выражениями. Буду делать это спринтами. Скорее всего, по одному-два проекту в месяц.
Что в итоге
На календаре 24 октября. Все ~100 сайтов работают на PHP 8.5.12. 32 легаси-зомби поднялись на таблетке за 20 минут деплоя.
Почти во всех проектах, были файлы начинающиеся с <? , а не с <?php. Странно. И ведь я сам это, в большинстве случаев, писал и даже не обращал на это внимания. А на PHP 8.5.12 это важно.
Конечно же null, которым PHP 8+ постоянно давиться. Благо, таких мест было не много и они были исправлены ручками за короткое время. Но, пару седых волос, мне они прибавили все же.
И конечно же count(), который встретился несколько раз, но тоже был подперт костылем из ?? [] и оставлен впокое.
В целом, все прошло, хоть и нервозно, но достаточно стабильно. Я ожидал намного большего ужаса и мрака, чем оказалось в реальности.
Чего и вам желаю!
Ну собственно, вот полный код Таблетки:
<?php/** * mysql-tablet.php — слой совместимости для легаси-функций mysql_*. * * Восстанавливает API удалённого в PHP 7.0 расширения ext/mysql, * реализуя его поверх mysqli. Позволяет запускать старый код без правок * на PHP 8.x, включая 8.5. * * Требования окружения: * - PHP >= 8.0 (используются str_starts_with() и str_contains()); * - включённое расширение mysqli. * * Побочные эффекты при подключении файла: * - отключается режим исключений mysqli (MYSQLI_REPORT_OFF); * - объявляются константы MYSQL_ASSOC, MYSQL_NUM, MYSQL_BOTH; * - объявляются функции mysql_*, если они отсутствуют в ядре. * * ВНИМАНИЕ: файл является «костылём» для легаси-кода и не защищает * от SQL-инъекций, уже присутствующих в этом коде. * * @package MysqlTablet * @author Сергей Абрамов и Макс-канал "Веб Мастерская" * @version 1.0.0 * @see https://www.php.net/manual/ru/book.mysqli.php * @see https://www.php.net/manual/ru/book.mysql.php *//** * Отключает выброс исключений mysqli. * * Легаси-код ожидает «тихий» возврат false и ручную проверку ошибок * через mysql_errno() / mysql_error(), поэтому режим исключений * (включён по умолчанию в PHP 8.1+) необходимо погасить. */if (function_exists('mysqli_report') && defined('MYSQLI_REPORT_OFF')) { mysqli_report(MYSQLI_REPORT_OFF);}/** * Эмулирует константу ext/mysql MYSQL_ASSOC (ассоциативная выборка). * * Значение берётся из MYSQLI_ASSOC; при его отсутствии используется * историческое числовое значение 1. */if (!defined('MYSQL_ASSOC')) { define('MYSQL_ASSOC', defined('MYSQLI_ASSOC') ? MYSQLI_ASSOC : 1);}/** * Эмулирует константу ext/mysql MYSQL_NUM (нумерованная выборка). * * Значение берётся из MYSQLI_NUM; при его отсутствии используется * историческое числовое значение 2. */if (!defined('MYSQL_NUM')) { define('MYSQL_NUM', defined('MYSQLI_NUM') ? MYSQLI_NUM : 2);}/** * Эмулирует константу ext/mysql MYSQL_BOTH (ассоциативная + нумерованная выборка). * * Значение берётся из MYSQLI_BOTH; при его отсутствии используется * историческое числовое значение 3. */if (!defined('MYSQL_BOTH')) { define('MYSQL_BOTH', defined('MYSQLI_BOTH') ? MYSQLI_BOTH : 3);}/** * Блок объявления функций-заглушек. * * Функции объявляются только при отсутствии одноимённых в ядре * (то есть на PHP >= 7.0, где ext/mysql удалено). Проверка * function_exists() защищает от фатальной ошибки повторного объявления. */if (!function_exists('mysql_connect')) { /** * Открывает соединение с MySQL-сервером (эмуляция mysql_connect()). * * Поддерживает исторические форматы строки сервера: * - «host:port» — явное указание порта; * - «:/path/to/socket» или «host:/path/to/socket» — UNIX-сокет; * - префикс «p:» — персистентное соединение. * * Успешное соединение сохраняется в $GLOBALS['mysql_oldstyle_link'] * и используется остальными функциями как соединение по умолчанию. * Ошибки соединения фиксируются в $GLOBALS['mysql_oldstyle_connect_errno'] * и $GLOBALS['mysql_oldstyle_connect_error'] и доступны через * mysql_errno() / mysql_error(). Исключения наружу не пробрасываются. * * @param string $server Хост сервера, опционально «:порт», «:/путь/к/сокету» и/или префикс «p:». * @param string $username Имя пользователя MySQL. * @param string $password Пароль пользователя MySQL. * @param bool $new_link Не используется; сохранён исключительно для совместимости сигнатуры. * @param int $client_flags Битовая маска клиентских флагов mysqli (MYSQLI_CLIENT_*). * * @return mysqli|false Объект соединения либо false при ошибке подключения. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * @global int $mysql_oldstyle_connect_errno Код последней ошибки соединения ($GLOBALS['mysql_oldstyle_connect_errno']). * @global string $mysql_oldstyle_connect_error Текст последней ошибки соединения ($GLOBALS['mysql_oldstyle_connect_error']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_real_connect() */ function mysql_connect($server, $username, $password, $new_link = false, $client_flags = 0) { try { if (!function_exists('mysqli_init')) { return false; } $host = (string) $server; $port = null; $socket = null; $persistentPrefix = ''; // Префикс «p:» означает персистентное соединение в mysqli. if (str_starts_with($host, 'p:')) { $persistentPrefix = 'p:'; $host = substr($host, 2); } // Разбор исторических форматов строки сервера ext/mysql. if ($host === '') { $host = 'localhost'; } elseif (str_starts_with($host, ':')) { $socket = substr($host, 1); $host = 'localhost'; } elseif (str_contains($host, ':')) { [$hostname, $extra] = explode(':', $host, 2); if ($hostname !== '') { $host = $hostname; } if ($extra !== '') { if ($extra[0] === '/') { $socket = $extra; // Всё, что начинается с «/», — путь к сокету. } else { $port = (int) $extra; // Иначе — номер порта. } } } $host = $persistentPrefix . $host; $link = mysqli_init(); if (!$link instanceof mysqli) { return false; } // «@» глушит warning'и: легаси само проверяет ошибки через mysql_errno(). $ok = @mysqli_real_connect( $link, $host, (string) $username, (string) $password, null, $port, $socket, (int) $client_flags ); if (!$ok) { $GLOBALS['mysql_oldstyle_connect_errno'] = function_exists('mysqli_connect_errno') ? mysqli_connect_errno() : 0; $GLOBALS['mysql_oldstyle_connect_error'] = function_exists('mysqli_connect_error') ? mysqli_connect_error() : ''; return false; } $GLOBALS['mysql_oldstyle_link'] = $link; $GLOBALS['mysql_oldstyle_connect_errno'] = 0; $GLOBALS['mysql_oldstyle_connect_error'] = ''; return $link; } catch (Throwable $e) { $GLOBALS['mysql_oldstyle_connect_errno'] = (int) $e->getCode(); $GLOBALS['mysql_oldstyle_connect_error'] = $e->getMessage(); return false; } } /** * Выбирает базу данных по умолчанию для соединения (эмуляция mysql_select_db()). * * @param string $database_name Имя базы данных. * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return bool true при успехе, false при ошибке или невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_select_db() */ function mysql_select_db($database_name, $link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return false; } return mysqli_select_db($link, (string) $database_name); } /** * Выполняет SQL-запрос (эмуляция mysql_query()). * * @param string $sql Текст SQL-запроса. * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return mysqli_result|bool Объект результата для SELECT-подобных запросов, * true для успешных запросов без результата, * false при ошибке или невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_query() */ function mysql_query($sql, $link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return false; } return mysqli_query($link, (string) $sql); } /** * Выбирает следующую строку результата в виде нумерованного массива (эмуляция mysql_fetch_row()). * * В отличие от mysqli_fetch_row() возвращает false вместо null при выходе * за конец результата — именно на такое поведение рассчитан легаси-код. * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * * @return array<int, string|null>|false Строка результата либо false, если строк больше нет * или передан не-результат. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_fetch_row() */ function mysql_fetch_row($res) { if (!$res instanceof mysqli_result) { return false; } $row = mysqli_fetch_row($res); return $row === null ? false : $row; } /** * Выбирает следующую строку результата в виде ассоциативного массива (эмуляция mysql_fetch_assoc()). * * В отличие от mysqli_fetch_assoc() возвращает false вместо null при выходе * за конец результата. * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * * @return array<string, string|null>|false Строка результата либо false, если строк больше нет * или передан не-результат. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_fetch_assoc() */ function mysql_fetch_assoc($res) { if (!$res instanceof mysqli_result) { return false; } $row = mysqli_fetch_assoc($res); return $row === null ? false : $row; } /** * Выбирает следующую строку результата в заданном формате (эмуляция mysql_fetch_array()). * * Исторические константы MYSQL_ASSOC / MYSQL_NUM / MYSQL_BOTH прозрачно * отображаются на MYSQLI_ASSOC / MYSQLI_NUM / MYSQLI_BOTH. * Возвращает false вместо null при выходе за конец результата. * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * @param int|null $result_type Одна из констант MYSQL_ASSOC, MYSQL_NUM, MYSQL_BOTH; * null эквивалентен MYSQL_BOTH. * * @return array<int|string, string|null>|false Строка результата либо false, если строк больше нет * или передан не-результат. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_fetch_array() */ function mysql_fetch_array($res, $result_type = null) { if (!$res instanceof mysqli_result) { return false; } if ($result_type === null) { $result_type = MYSQL_BOTH; } $result_type = (int) $result_type; // Маппинг исторических констант ext/mysql на константы mysqli. $map = [ MYSQL_ASSOC => MYSQLI_ASSOC, MYSQL_NUM => MYSQLI_NUM, MYSQL_BOTH => MYSQLI_BOTH, ]; $mode = $map[$result_type] ?? MYSQLI_BOTH; $row = mysqli_fetch_array($res, $mode); return $row === null ? false : $row; } /** * Выбирает следующую строку результата в виде объекта (эмуляция mysql_fetch_object()). * * Возвращает false вместо null при выходе за конец результата. * Ошибки создания экземпляра класса перехватываются и также приводят к false. * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * @param string $classname Имя класса создаваемого объекта. * @param array<int, mixed> $params Аргументы конструктора класса. * * @return object|false Экземпляр $classname с полями строки либо false, если строк больше нет, * передан не-результат или не удалось создать объект. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_fetch_object() */ function mysql_fetch_object($res, $classname = 'stdClass', $params = []) { if (!$res instanceof mysqli_result) { return false; } try { $object = mysqli_fetch_object( $res, (string) $classname, (array) $params ); } catch (Throwable $e) { return false; } return $object === null ? false : $object; } /** * Возвращает число строк, затронутых последним запросом (эмуляция mysql_affected_rows()). * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return int Число затронутых строк; -1 при ошибке запроса или невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_affected_rows() */ function mysql_affected_rows($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return -1; } return mysqli_affected_rows($link); } /** * Возвращает ID строки, вставленной последним запросом (эмуляция mysql_insert_id()). * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return int|string ID последней вставленной строки (string для значений BIGINT); * 0 при невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_insert_id() */ function mysql_insert_id($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return 0; } return mysqli_insert_id($link); } /** * Возвращает код последней ошибки (эмуляция mysql_errno()). * * Если соединение отсутствует или невалидно, отдаёт код ошибки соединения, * сохранённый функцией mysql_connect() в $GLOBALS. * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return int Код последней ошибки; 0, если ошибок не было. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * @global int $mysql_oldstyle_connect_errno Код последней ошибки соединения ($GLOBALS['mysql_oldstyle_connect_errno']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_errno() */ function mysql_errno($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return (int) ($GLOBALS['mysql_oldstyle_connect_errno'] ?? 0); } return mysqli_errno($link); } /** * Возвращает текст последней ошибки (эмуляция mysql_error()). * * Если соединение отсутствует или невалидно, отдаёт текст ошибки соединения, * сохранённый функцией mysql_connect() в $GLOBALS. * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return string Текст последней ошибки; пустая строка, если ошибок не было. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * @global string $mysql_oldstyle_connect_error Текст последней ошибки соединения ($GLOBALS['mysql_oldstyle_connect_error']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_error() */ function mysql_error($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return (string) ($GLOBALS['mysql_oldstyle_connect_error'] ?? ''); } return mysqli_error($link); } /** * Возвращает число строк в результате запроса (эмуляция mysql_num_rows()). * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * * @return int|false Число строк в результате либо false, если передан не-результат. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_num_rows() */ function mysql_num_rows($res) { if (!$res instanceof mysqli_result) { return false; } return mysqli_num_rows($res); } /** * Освобождает память, занятую результатом запроса (эмуляция mysql_free_result()). * * @param mysqli_result|mixed $res Результат запроса, возвращённый mysql_query(). * * @return bool true, если память освобождена; false, если передан не-результат. * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_free_result() */ function mysql_free_result($res) { if (!$res instanceof mysqli_result) { return false; } mysqli_free_result($res); return true; } /** * Закрывает соединение с сервером (эмуляция mysql_close()). * * Если закрывается соединение по умолчанию, оно дополнительно удаляется * из $GLOBALS, чтобы остальные функции не использовали «мёртвый» линк. * Закрытие невалидного соединения считается успешным (true), как в ext/mysql. * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return bool true при успешном закрытии (или невалидном соединении), false при ошибке. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_close() */ function mysql_close($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return true; } $result = mysqli_close($link); if (isset($GLOBALS['mysql_oldstyle_link']) && $GLOBALS['mysql_oldstyle_link'] === $link) { unset($GLOBALS['mysql_oldstyle_link']); } return $result; } /** * Экранирует специальные символы в строке для использования в SQL (эмуляция mysql_real_escape_string()). * * @param string $sql Исходная строка. * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return string|false Экранированная строка либо false при невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте подготовленные выражения mysqli/PDO. * @see mysqli_real_escape_string() */ function mysql_real_escape_string($sql, $link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return false; } return mysqli_real_escape_string($link, (string) $sql); } /** * Возвращает версию MySQL-сервера (эмуляция mysql_get_server_info()). * * @param mysqli|null $link Соединение; null — соединение по умолчанию из $GLOBALS. * * @return string|false Строка версии сервера либо false при невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_get_server_info() */ function mysql_get_server_info($link = null) { if ($link === null) { $link = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link instanceof mysqli) { return false; } return mysqli_get_server_info($link); } /** * Устанавливает кодировку соединения (эмуляция mysql_set_charset()). * * @param string $charset Имя кодировки (например, «utf8mb4»). * @param mysqli|null $link_identifier Соединение; null — соединение по умолчанию из $GLOBALS. * * @return bool true при успехе, false при ошибке или невалидном соединении. * * @global mysqli|null $mysql_oldstyle_link Соединение по умолчанию ($GLOBALS['mysql_oldstyle_link']). * * @deprecated Только для совместимости с легаси; в новом коде используйте mysqli или PDO. * @see mysqli_set_charset() */ function mysql_set_charset($charset, $link_identifier = null) { if ($link_identifier === null) { $link_identifier = $GLOBALS['mysql_oldstyle_link'] ?? null; } if (!$link_identifier instanceof mysqli) { return false; } return mysqli_set_charset($link_identifier, (string) $charset); }}?>
ссылка на оригинал статьи https://habr.com/ru/articles/1098930/