Avatar ·

Почему нельзя использовать устаревшие функции mysql_* в PHP и чем их заменить?

Всем привет! Я начинающий PHP-разработчик, учусь по старым учебникам. Во всех примерах подключение к базе данных MySQL выглядит так: ```php $link = mysql_connect('localhost', 'root', ''); mysql_select_db('my_database', $link); $result = mysql_query("SELECT * FROM users WHERE id = " . $_GET['id']); while ($row = mysql_fetch_assoc($result)) { echo $row['name']; } ``` Всё работает, но я вижу, что в современном PHP эти функции вызывают ошибки. В логах пишет, что они устарели. На форумах говорят, что их вообще удалили в PHP 7. Я понимаю, что нужно переходить на что-то новое, но не понимаю: в чём конкретно проблема mysql_*? Ведь они работают. Чем они опасны? И что использовать вместо них — mysqli или PDO? В чём разница? Буду благодарен за подробное объяснение с примерами, чтобы я мог переписать свой код правильно. <<>>

Главные причины отказаться от mysql_*

Вы столкнулись с классической проблемой устаревшего кода. Функции mysql_* удалены из PHP 7.0 и выше. Но даже если бы они остались, их категорически нельзя использовать по трём основным причинам:

  • Отсутствие поддержки подготовленных запросов — вы вынуждены вручную экранировать данные, что часто забывают делать;
  • SQL-инъекции — ваш код уязвим, как показано в примере с $_GET['id'];
  • Устаревшее API — нет поддержки хранимых процедур, множественных запросов, транзакций.

Ваш пример с конкатенацией $_GET['id'] — это прямая брешь в безопасности. Злоумышленник может передать ?id=1 UNION SELECT * FROM users и получить все данные. Функция mysql_real_escape_string() не спасает на 100%, особенно при работе с кодировками.

Правильное решение: используем PDO

Современный стандарт — PDO (PHP Data Objects) или MySQLi. Я рекомендую PDO, потому что он поддерживает разные СУБД (MySQL, PostgreSQL, SQLite). Вот как переписать ваш код:

<?php
// Подключение с обработкой ошибок
try {
    $pdo = new PDO('mysql:host=localhost;dbname=my_database;charset=utf8', 'root', '');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die('Ошибка подключения: ' . $e->getMessage());
}

// Безопасный запрос с подготовленным выражением
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => $_GET['id']]); // Данные экранируются автоматически

// Получение результатов
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo htmlspecialchars($row['name'], ENT_QUOTES, 'UTF-8'); // Защита от XSS
}
?>

Преимущества PDO перед mysql_*

  • Подготовленные запросы — данные и SQL-код разделены, инъекции невозможны;
  • Именованные плейсхолдеры (:id) — код читаемее;
  • Режимы выборки — FETCH_ASSOC, FETCH_OBJ и другие;
  • Транзакции — $pdo->beginTransaction();
  • Обработка ошибок через исключения — вы всегда знаете, что пошло не так.

Альтернатива: MySQLi (объектный стиль)

Если вы точно работаете только с MySQL, можно использовать MySQLi:

<?php
$mysqli = new mysqli('localhost', 'root', '', 'my_database');
if ($mysqli->connect_error) {
    die('Ошибка: ' . $mysqli->connect_error);
}

$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param('i', $_GET['id']);
$stmt->execute();
$result = $stmt->get_result();

while ($row = $result->fetch_assoc()) {
    echo htmlspecialchars($row['name']);
}
?>

Итог: прекратите использовать mysql_* немедленно. Перепишите старый код на PDO или MySQLi. Это не просто «новый стандарт» — это вопрос безопасности вашего сайта и данных пользователей.

<<>>

Дополнение: почему mysql_* опасны даже с экранированием

Многие начинающие думают: «Я же использую mysql_real_escape_string() — значит, всё безопасно». Это опасное заблуждение. Рассмотрим пример:

$id = mysql_real_escape_string($_GET['id']);
$query = "SELECT * FROM users WHERE id = $id"; // Всё ещё опасно!

Проблема в том, что mysql_real_escape_string() зависит от текущей кодировки соединения. Если кодировка соединения отличается от кодировки данных (например, из-за SET NAMES), экранирование может быть обойдено. Известны атаки через многобайтовые кодировки (GBK, Big5), когда злоумышленники используют символы, которые «съедают» экранирующий слеш.

Кроме того, mysql_* не поддерживает:

  • Привязку параметров по типу (integer, string);
  • Автоматическое преобразование типов;
  • Пул соединений и повторное использование.

Вывод: даже если ваш код кажется безопасным, используйте PDO или MySQLi. Подготовленные запросы — это не рекомендация, а обязательное требование для любого современного PHP-приложения. Не полагайтесь на «ручное» экранирование — доверьте эту работу драйверу базы данных.