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

Oct 12 2012

Каковы технические причины, по которым нельзя использовать mysql_*функции? (например mysql_query(), mysql_connect()или mysql_real_escape_string())?

Зачем мне использовать что-то еще, даже если они работают на моем сайте?

Если они не работают на моем сайте, почему я получаю такие ошибки, как

Предупреждение: mysql_connect (): нет такого файла или каталога

Ответы

2118 Quentin Oct 12 2012 at 20:23

Расширение MySQL:

  • Не в активной разработке
  • Является ли официально устаревшим в РНР 5.5 (выпущен в июне 2013 года).
  • Был удален полностью от PHP 7.0 (выпущена в декабре 2015 года)
    • Это означает, что по состоянию на 31 декабря 2018 года он не существует ни в одной поддерживаемой версии PHP. Если вы используете версию PHP, которая его поддерживает, значит, вы используете версию, в которой не устранены проблемы с безопасностью.
  • Отсутствует интерфейс OO
  • Не поддерживает:
    • Неблокирующие, асинхронные запросы
    • Подготовленные операторы или параметризованные запросы
    • Хранимые процедуры
    • Множественные заявления
    • Сделки
    • «Новый» метод аутентификации по паролю (по умолчанию в MySQL 5.6; требуется в 5.7)
    • Любая из новых функций в MySQL 5.1 или новее

Поскольку он устарел, его использование делает ваш код менее надежным в будущем.

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

См. Сравнение расширений SQL .

1303 NullPoiиteя Jan 01 2013 at 18:52

PHP предлагает три различных API для подключения к MySQL. Это mysql(удалены из PHP 7) mysqli, и PDOрасширения.

Эти mysql_*функции используются очень популярны, но их использование не рекомендуется больше. Команда документации обсуждает ситуацию с безопасностью базы данных, и обучение пользователей отказу от широко используемого расширения ext / mysql является частью этого (проверьте php.internals: отказ от ext / mysql ).

И позже команда PHP разработчик принял решение генерировать E_DEPRECATEDошибки , когда пользователи подключаются к MySQL, будь то через mysql_connect(), mysql_pconnect()или неявное функциональность подключения встроенного в ext/mysql.

ext/mysqlбыл официально устаревшим PHP 5.5 и был удален в PHP 7 .

Видите красную коробку?

Когда вы переходите на любую mysql_*страницу руководства по функциям, вы видите красную рамку, поясняющую, что ее больше не следует использовать.

Почему


Отказ ext/mysql- это не только безопасность, но и доступ ко всем функциям базы данных MySQL.

ext/mysqlбыл построен для MySQL 3.23 и с тех пор получил очень мало дополнений, в основном сохраняя совместимость с этой старой версией, что немного затрудняет поддержку кода. Отсутствующие функции, которые не поддерживаются ext/mysqlinclude: ( из руководства по PHP ).

  • Хранимые процедуры (не могут обрабатывать несколько наборов результатов)
  • Подготовленные заявления
  • Шифрование (SSL)
  • Сжатие
  • Полная поддержка Charset

Причина не использовать mysql_*функцию :

  • Не в активной разработке
  • Удалено с PHP 7
  • Отсутствует интерфейс OO
  • Не поддерживает неблокирующие асинхронные запросы.
  • Не поддерживает подготовленные операторы или параметризованные запросы
  • Не поддерживает хранимые процедуры
  • Не поддерживает несколько утверждений
  • Не поддерживает транзакции
  • Не поддерживает все функции MySQL 5.1.

Выше цитата из ответа Квентина

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

См. Сравнение расширений SQL .


Подавление предупреждений об устаревании

Пока код конвертируется в MySQLi/ PDO, E_DEPRECATEDошибки можно подавить, установив error_reportingв php.ini исключениеE_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Обратите внимание, что это также скроет другие предупреждения об устаревании , которые, однако, могут относиться не только к MySQL. ( из руководства по PHP )

Статья PDO vs. MySQLi: что следует использовать? от Деяна Марьянович поможет вам выбрать.

И лучший способ - PDOсейчас я пишу простой PDOучебник.


Простое и короткое руководство по PDO


В. Первый вопрос в моей голове был: что такое `PDO`?

A. « PDO - объекты данных PHP - это уровень доступа к базе данных, обеспечивающий единый метод доступа к нескольким базам данных».


Подключение к MySQL

С mysql_*функцией, или мы можем сказать это по-старому (устарело в PHP 5.5 и выше)

$link = mysql_connect('localhost', 'user', 'pass'); mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

С помощью PDO: Все, что вам нужно сделать, это создать новый PDOобъект. Конструктор принимает параметры для указания источника базы данных PDO«s конструктора в основном принимает четыре параметра , которые DSN(имя источника данных) и , возможно username, password.

Здесь, я думаю, вы знакомы со всем, кроме DSN; это новинка PDO. A DSN- это в основном строка параметров, которые указывают, PDOкакой драйвер использовать, и сведения о подключении. Для получения дополнительной информации проверьте PDO MySQL DSN .

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Примечание: вы также можете использовать charset=UTF-8, но иногда это вызывает ошибку, поэтому лучше использовать utf8.

Если есть какая-либо ошибка соединения, он выдаст PDOExceptionобъект, который можно будет поймать для Exceptionдальнейшей обработки .

Хорошее прочтение : Подключения и управление подключениями ¶

Вы также можете передать несколько параметров драйвера в виде массива в четвертый параметр. Я рекомендую передать параметр, который переводит PDOв режим исключения. Поскольку некоторые PDOдрайверы не поддерживают собственные подготовленные операторы, поэтому PDOвыполняет эмуляцию подготовки. Он также позволяет вручную включить эту эмуляцию. Чтобы использовать собственные подготовленные операторы на стороне сервера, вы должны явно установить его false.

Другой - отключить эмуляцию подготовки, которая включена в MySQLдрайвере по умолчанию, но эмуляция подготовки должна быть отключена для PDOбезопасного использования .

Позже я объясню, почему следует отключить эмуляцию подготовки. Чтобы узнать причину, проверьте этот пост .

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

Ниже приведен пример того, как это можно сделать:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Можем ли мы установить атрибуты после построения PDO?

Да , мы также можем установить некоторые атрибуты после построения PDO с помощью setAttributeметода:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Обработка ошибок


Обработка ошибок намного проще в PDOчем mysql_*.

Обычная практика при использовании mysql_*:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()не лучший способ справиться с ошибкой, поскольку мы не можем справиться с ней в die. Он просто внезапно завершит скрипт, а затем отобразит ошибку на экране, который вы обычно НЕ хотите показывать своим конечным пользователям, и позволит кровавым хакерам обнаружить вашу схему. В качестве альтернативы возвращаемые значения mysql_*функций часто можно использовать в сочетании с mysql_error () для обработки ошибок.

PDOпредлагает лучшее решение: исключения. Все, что мы делаем, PDOдолжно быть заключено в блок try- catch. Мы можем принудительно перейти PDOв один из трех режимов ошибки, установив атрибут режима ошибки. Ниже приведены три режима обработки ошибок.

  • PDO::ERRMODE_SILENT. Он просто устанавливает коды ошибок и действует примерно так же, как mysql_*когда вы должны проверять каждый результат, а затем просматривать $db->errorInfo();подробности ошибки.
  • PDO::ERRMODE_WARNINGПоднять E_WARNING. (Предупреждения во время выполнения (нефатальные ошибки). Выполнение скрипта не останавливается.)
  • PDO::ERRMODE_EXCEPTION: Выбрасывать исключения. Он представляет ошибку, вызванную PDO. Вы не должны выбрасывать PDOExceptionиз собственного кода. См. Исключения для получения дополнительной информации об исключениях в PHP. Он действует очень похоже or die(mysql_error());, когда его не поймают. Но, в отличие от этого or die(), их PDOExceptionможно поймать и изящно обработать, если вы захотите это сделать.

Хорошее чтение :

  • Ошибки и обработка ошибок ¶
  • Класс PDOException ¶
  • Исключения ¶

Нравиться:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT ); $stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

И вы можете обернуть его try- catch, как показано ниже:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) { echo "An Error occured!"; //User friendly message/message you want to show to user some_logging_function($ex->getMessage());
}

Вам не нужно заниматься try- catchпрямо сейчас. Вы можете поймать его в любое удобное время, но я настоятельно рекомендую вам использовать try- catch. Также может иметь смысл поймать его за пределами функции, которая вызывает PDOматериал:

function data_fun($db) { $stmt = $db->query("SELECT * FROM table"); return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db); } catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

Кроме того, вы можете справиться or die()или, можно сказать, нравится mysql_*, но это будет действительно разнообразно. Вы можете скрыть опасные сообщения об ошибках в производственной среде, повернув display_errors offи просто прочитав журнал ошибок.

Теперь, после прочтения всех вещей выше, вы, вероятно , думаете: что это такое , когда я просто хочу , чтобы начать опираясь простым SELECT, INSERT, UPDATE, или DELETEзаявление? Не волнуйтесь, мы идем:


Выбор данных

Итак, что вы делаете mysql_*:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error()); $num_rows = mysql_num_rows($result); while($row = mysql_fetch_assoc($result)) { echo $row['field1'];
}

Теперь PDOвы можете сделать это так:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

Или же

<?php
$stmt = $db->query('SELECT * FROM table'); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); //Use $results

Примечание . Если вы используете метод, подобный приведенному ниже ( query()), этот метод возвращает PDOStatementобъект. Поэтому, если вы хотите получить результат, используйте его, как указано выше.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

В PDO Data он получается с помощью ->fetch()метода дескриптора оператора. Перед вызовом fetch лучший подход - сообщить PDO, как вы хотите получать данные. В следующем разделе я объясняю это.

Режимы получения

Обратите внимание на использование PDO::FETCH_ASSOCв приведенном выше коде fetch()и fetchAll(). Это говорит PDOо том, что нужно вернуть строки как ассоциативный массив с именами полей в качестве ключей. Есть много других режимов выборки, которые я объясню один за другим.

Прежде всего, я объясню, как выбрать режим выборки:

 $stmt->fetch(PDO::FETCH_ASSOC)

Выше я использовал fetch(). Вы также можете использовать:

  • PDOStatement::fetchAll() - Возвращает массив, содержащий все строки набора результатов
  • PDOStatement::fetchColumn() - Возвращает один столбец из следующей строки набора результатов
  • PDOStatement::fetchObject() - Выбирает следующую строку и возвращает ее как объект.
  • PDOStatement::setFetchMode() - Установите режим выборки по умолчанию для этого оператора

Теперь я перехожу в режим загрузки:

  • PDO::FETCH_ASSOC: возвращает массив, проиндексированный по имени столбца, как возвращено в вашем наборе результатов
  • PDO::FETCH_BOTH (по умолчанию): возвращает массив, проиндексированный как по имени столбца, так и по номеру столбца с индексом 0, как возвращено в вашем наборе результатов

Есть еще больше вариантов! Прочтите обо всех них в PDOStatementдокументации по Fetch. .

Получение количества строк :

Вместо использования mysql_num_rowsдля получения количества возвращенных строк вы можете получить PDOStatementи сделать rowCount(), например:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

Получение последнего введенного идентификатора

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')"); $insertId = $db->lastInsertId();

Операторы вставки и обновления или удаления

Что мы делаем в mysql_*функции:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

А в pdo то же самое можно сделать:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'"); echo $affected_rows;

В приведенном выше запросе PDO::execвыполните оператор SQL и вернет количество затронутых строк.

Вставка и удаление будут рассмотрены позже.

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


Подготовленные заявления

В. Что такое подготовленная выписка и зачем она мне нужна?
A. Подготовленный оператор - это предварительно скомпилированный оператор SQL, который может выполняться несколько раз, отправляя на сервер только данные.

Типичный рабочий процесс использования подготовленного оператора выглядит следующим образом ( цитата из Википедии, три 3 пункта ):

  1. Подготовить : шаблон отчета создается приложением и отправляется в систему управления базами данных (СУБД). Некоторые значения остаются неопределенными, они называются параметрами, заполнителями или связывающими переменными (помечены ?ниже):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. СУБД анализирует, компилирует и выполняет оптимизацию запроса в шаблоне оператора и сохраняет результат, не выполняя его.

  3. Выполнить : позже приложение предоставляет (или связывает) значения для параметров, и СУБД выполняет оператор (возможно, возвращая результат). Приложение может выполнять оператор сколько угодно раз с разными значениями. В этом примере он может предоставить «хлеб» для первого параметра и 1.00для второго параметра.

Вы можете использовать подготовленный оператор, включив заполнители в свой SQL. В основном есть три без заполнителей (не пытайтесь это сделать с переменной выше), один с безымянными заполнителями и один с именованными заполнителями.

В. Итак, что же называется заполнителями и как их использовать?
A. Именованные заполнители. Вместо вопросительных знаков используйте описательные имена, перед которыми стоит двоеточие. Нас не волнует позиция / порядок значений в заполнителе имени:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

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

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Еще одна приятная особенность для OOPдрузей состоит в том, что именованные заполнители имеют возможность вставлять объекты непосредственно в вашу базу данных, если свойства соответствуют именованным полям. Например:

class person {
    public $name;
    public $add; function __construct($a,$b) { $this->name = $a; $this->add = $b; } } $demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

В. Итак, что такое безымянные заполнители и как их использовать?
A. Приведем пример:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

и

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)"); $stmt->execute(array('john', '29 bla district'));

В приведенном выше примере вы можете увидеть их ?вместо имени, как в заполнителе имени. Теперь в первом примере мы назначаем переменные различным заполнителям ( $stmt->bindValue(1, $name, PDO::PARAM_STR);). Затем мы присваиваем значения этим заполнителям и выполняем инструкцию. Во втором примере первый элемент массива переходит к первому, ?а второй - ко второму ?.

ПРИМЕЧАНИЕ . В безымянных заполнителях мы должны позаботиться о правильном порядке элементов в массиве, который мы передаем PDOStatement::execute()методу.


SELECT, INSERT, UPDATE, DELETEПодготовлены запросы

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)"); $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute(); $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?"); $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

ПРИМЕЧАНИЕ:

Однако PDOи / или MySQLiне полностью безопасны. Проверить ответ Достаточно ли подготовленных операторов PDO для предотвращения внедрения SQL? пользователя ircmaxell . Также я процитирую часть его ответа:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
305 Madara'sGhost Oct 12 2012 at 20:28

Во-первых, давайте начнем со стандартного комментария, который мы даем всем:

Пожалуйста, не используйте mysql_*функции в новом коде . Они больше не поддерживаются и официально устарели . Видите красную рамку ? Вместо этогоузнайте о подготовленных операторах и используйте PDO или MySQLi - эта статья поможет вам решить, какие. Если вы выберете PDO, вот хороший учебник .

Давайте рассмотрим это предложение за предложением и объясним:

  • Они больше не поддерживаются и официально устарели.

    Это означает, что сообщество PHP постепенно отказывается от поддержки этих очень старых функций. Скорее всего, их не будет в будущей (недавней) версии PHP! Продолжение использования этих функций может сломать ваш код в (не так уж) далеком будущем.

    НОВЫЙ! - ext / mysql официально устарел, начиная с PHP 5.5!

    Новее! ext / mysql был удален в PHP 7 .

  • Вместо этого вы должны узнать о подготовленных заявлениях

    mysql_*extension не поддерживает подготовленные операторы , что (помимо прочего) является очень эффективной мерой против внедрения SQL-кода . Он исправил очень серьезную уязвимость в зависимых от MySQL приложениях, которая позволяет злоумышленникам получить доступ к вашему скрипту и выполнить любой возможный запрос к вашей базе данных.

    Дополнительные сведения см. В разделе Как предотвратить внедрение SQL-кода в PHP?

  • Видите красную коробку?

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

  • Используйте PDO или MySQLi

    Существуют лучшие, более надежные и хорошо продуманные альтернативы: PDO - объект базы данных PHP , который предлагает полный ООП-подход к взаимодействию с базой данных, и MySQLi , который является улучшением, специфичным для MySQL.

221 mario Dec 25 2013 at 06:30

Легкость использования

Аналитические и синтетические причины уже упоминались. Для новичков есть более серьезный стимул отказаться от использования устаревших функций mysql_.

Современные API баз данных просто проще в использовании.

В основном это связанные параметры, которые могут упростить код. А с отличными учебными пособиями (как показано выше) переход на PDO не слишком труден.

Однако одновременное переписывание более крупной базы кода требует времени. Смысл существования этой промежуточной альтернативы:

Эквивалентные функции pdo_ * вместо mysql_ *

Используя < pdo_mysql.php >, вы можете переключиться со старых функций mysql_ с минимальными усилиями . Он добавляет pdo_обертки функций, которые заменяют их mysql_аналоги.

  1. Просто в каждом скрипте вызова, который должен взаимодействовать с базой данных.include_once("pdo_mysql.php");

  2. Удалите повсюдуmysql_ префикс функции и замените его на .pdo_

    • mysql_connect() становится pdo_connect()
    • mysql_query() становится pdo_query()
    • mysql_num_rows() становится pdo_num_rows()
    • mysql_insert_id() становится pdo_insert_id()
    • mysql_fetch_array() становится pdo_fetch_array()
    • mysql_fetch_assoc() становится pdo_fetch_assoc()
    • mysql_real_escape_string() становится pdo_real_escape_string()
    • и так далее...

  3. Ваш код будет работать одинаково и в основном выглядеть одинаково:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages"); while ($row = pdo_fetch_assoc($result)) { print "$row[title] - $row[html]";
    }
    

И вуаля.
Ваш код использует PDO.
Пришло время действительно использовать это.

Связанные параметры легко использовать

Вам просто нужен менее громоздкий API.

pdo_query()добавляет очень простую поддержку связанных параметров. Преобразование старого кода несложно:

Переместите переменные из строки SQL.

  • Добавьте их как параметры функции, разделенные запятыми, в pdo_query().
  • Поместите вопросительные знаки ?вместо переменных вместо переменных.
  • Избавьтесь от 'одинарных кавычек, которые ранее заключали строковые значения / переменные.

Преимущество становится более очевидным для более длинного кода.

Часто строковые переменные не просто интерполируются в SQL, но объединяются с экранирующими вызовами между ними.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" . pdo_real_escape_string($root) . "' ORDER BY date")

С ?применением заполнителей вам не нужно об этом беспокоиться:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Помните, что pdo_ * по-прежнему разрешает либо или .
Просто не экранируйте переменную и не связывайте ее в том же запросе.

  • Функция заполнителя обеспечивается реальным PDO, стоящим за ним.
  • Таким образом, :namedпозже также разрешены списки заполнителей.

Что еще более важно, вы можете безопасно передавать переменные $ _REQUEST [] за любым запросом. Когда отправленные <form>поля точно соответствуют структуре базы данных, она еще короче:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

Такая простота. Но давайте вернемся к еще нескольким советам по переписыванию и техническим причинам того, почему вы можете захотеть избавиться mysql_и сбежать.

Исправьте или удалите любую sanitize()функцию старой школы

После того, как вы преобразовали все mysql_вызовы в pdo_queryс привязанными параметрами, удалите все избыточные pdo_real_escape_stringвызовы.

В частности, вы должны исправить любые sanitizeили cleanили filterThisили clean_dataфункции, рекламируемые в устаревших руководствах, в той или иной форме:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Самая вопиющая ошибка здесь - отсутствие документации. Что еще более важно, порядок фильтрации был совершенно неправильным.

  • Правильный порядок был бы таков: устаревший stripslashesкак самый внутренний вызов, затем trim, впоследствии strip_tags, htmlentitiesдля контекста вывода, и только, наконец, _escape_stringпоскольку его приложение должно непосредственно предшествовать взаимному синтаксическому анализу SQL.

  • Но в качестве первого шага просто избавьтесь от_real_escape_string звонка.

  • Возможно, вам придется sanitize()пока оставить остальную часть вашей функции, если ваша база данных и поток приложения ожидают HTML-контекстно-зависимые строки. Добавьте комментарий, что отныне применяется только экранирование HTML.

  • Обработка строки / значения делегируется PDO и его параметризованным операторам.

  • Если stripslashes()в вашей функции очистки было какое-либо упоминание о нем , это может указывать на надзор более высокого уровня.

    • Обычно это использовалось для отмены повреждений (двойное экранирование) от устаревшего magic_quotes. Что, однако, лучше всего фиксировать централизованно , а не построчно.

    • Используйте один из подходов разворота пользовательского пространства . Затем удалите stripslashes()в sanitizeфункции.

    Историческая справка о magic_quotes. Эта функция по праву устарела. Однако это часто неправильно изображается как отказавшая функция безопасности . Но magic_quotes - такая же неудачная функция безопасности, как теннисные мячи как источник питания. Это просто не было их целью.

    Первоначальная реализация в PHP2 / FI представила ее явно, просто « кавычки будут автоматически экранированы, что упрощает передачу данных формы непосредственно в запросы msql ». Примечательно, что использование с mSQL было случайно безопасно , так как поддерживалось только ASCII.
    Затем PHP3 / Zend повторно представили magic_quotes для MySQL и неправильно задокументировали его. Но изначально это была всего лишь функция удобства , а не безопасность.

Чем отличаются подготовленные заявления

Когда вы зашифровываете строковые переменные в SQL-запросах, это не только усложняется для вас. Для MySQL это также лишние усилия, чтобы снова разделить код и данные.

SQL-инъекции - это просто утечка данных в контекст кода . Сервер базы данных не может позже определить, где PHP изначально вставлял переменные между предложениями запроса.

С помощью связанных параметров вы разделяете код SQL и значения контекста SQL в своем коде PHP. Но он не перетасовывается снова за кулисами (кроме PDO :: EMULATE_PREPARES). Ваша база данных получает неизменные команды SQL и значения переменных 1: 1.

Хотя в этом ответе подчеркивается, что вы должны заботиться о преимуществах удобочитаемости при отбрасывании mysql_. Иногда также наблюдается преимущество в производительности (повторяющиеся INSERT только с разными значениями) из-за этого видимого и технического разделения данных / кода.

Помните, что привязка параметров по-прежнему не является волшебным универсальным решением против всех SQL-инъекций. Он обрабатывает наиболее распространенное использование данных / значений. Но не может занести в белый список имена столбцов / идентификаторы таблиц, помочь с построением динамических предложений или просто списки значений массива.

Использование гибридного PDO

Эти pdo_*функции-оболочки создают удобный для программирования временный API. (Это в значительной степени то, что MYSQLIмогло бы быть, если бы не идиосинкразический сдвиг сигнатуры функции). Они также чаще всего выставляют реальный PDO.
Переписывание не должно останавливаться на использовании новых имен функций pdo_. Вы можете один за другим переводить каждый pdo_query () в простой вызов $ pdo-> prepare () -> execute ().

Однако лучше начать с упрощения снова. Например, обычная выборка результатов:

$result = pdo_query("SELECT * FROM tbl"); while ($row = pdo_fetch_assoc($result)) {

Можно заменить простой итерацией foreach:

foreach ($result as $row) {

Или, еще лучше, прямое и полное извлечение массива:

$result->fetchAll();

В большинстве случаев вы получите более полезные предупреждения, чем обычно выдают PDO или mysql_ после неудачных запросов.

Другие варианты

Мы надеемся, что это наглядно продемонстрировало некоторые практические причины и достойный путь для отказа mysql_.

Простое переключение на pdo не совсем помогает . pdo_query()также является его интерфейсом.

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

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

Если вы хотите еще больше упростить взаимодействие с базой данных, стоит попробовать такие мапперы, как Paris / Idiorm . Точно так же, как никто больше не использует мягкий DOM в JavaScript, в настоящее время вам не нужно присматривать за необработанным интерфейсом базы данных.

150 Alnitak Oct 12 2012 at 20:22

Эти mysql_функции:

  1. устарели - они больше не обслуживаются
  2. не позволяют вам легко перейти на другую базу данных
  3. не поддерживают подготовленные операторы, поэтому
  4. поощрять программистов использовать конкатенацию для построения запросов, что приводит к уязвимости SQL-инъекций
109 YourCommonSense Jan 02 2013 at 00:42

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

  • неблокирующие, асинхронные запросы
  • хранимые процедуры, возвращающие несколько наборов результатов
  • Шифрование (SSL)
  • Сжатие

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

Тем не менее, есть также некоторые нетехнические проблемы, которые могут немного усложнить ваш опыт.

  • дальнейшее использование этих функций с современными версиями PHP приведет к появлению уведомлений об устаревшем уровне. Их просто можно отключить.
  • в далеком будущем они могут быть удалены из стандартной сборки PHP. Это тоже не имеет большого значения, поскольку mydsql ext будет перемещен в PECL, и каждый хостер будет счастлив скомпилировать PHP с ним, поскольку они не хотят терять клиентов, чьи сайты работали десятилетиями.
  • сильное сопротивление со стороны сообщества Stackoverflow. Каждый раз, когда вы упоминаете об этих честных функциях, вам говорят, что они находятся под строгим табу.
  • будучи средним пользователем PHP, вы, скорее всего, ошибетесь в использовании этих функций. Просто из-за всех этих многочисленных руководств и руководств, которые учат вас неправильному пути. Не сами функции - я должен это подчеркнуть, - а то, как они используются.

Последняя проблема - проблема.
Но, на мой взгляд, и предлагаемое решение ничем не лучше.
Мне кажется слишком идеалистической мечта, что все эти пользователи PHP сразу научатся правильно обрабатывать SQL-запросы. Скорее всего, они просто механически изменили бы mysql_ * на mysqli_ *, оставив тот же подход . Тем более, что mysqli делает использование подготовленных операторов невероятно болезненным и хлопотным.
Не говоря уже о том, что собственных подготовленных операторов недостаточно для защиты от SQL-инъекций, и ни mysqli, ни PDO не предлагают решения.

Поэтому вместо того, чтобы бороться с этим честным расширением, я предпочел бы бороться с неправильными методами и обучать людей правильным путям.

Также есть несколько ложных или несущественных причин, например

  • Не поддерживает хранимые процедуры (мы использовали mysql_query("CALL my_proc");целую вечность)
  • Не поддерживает транзакции (как указано выше)
  • Не поддерживает множественные утверждения (кому они нужны?)
  • Не в стадии активного развития (так что? Делает это влияет на вас в любом практическом плане?)
  • Отсутствует объектно-ориентированный интерфейс (на создание - дело нескольких часов)
  • Не поддерживает подготовленные операторы или параметризованные запросы.

Последнее - интересный момент. Хотя mysql ext не поддерживает собственные подготовленные операторы, они не требуются для безопасности. Мы можем легко подделать подготовленные операторы, используя ручные заполнители (как это делает PDO):

function paraQuery()
{
    $args = func_get_args(); $query = array_shift($args); $query = str_replace("%s","'%s'",$query); foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val); } $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result) { throw new Exception(mysql_error()." [$query]");
    }
    return $result; } $query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

вуаля , все параметризовано и безопасно.

Но ладно, если вам не нравится красное поле в руководстве, возникает проблема выбора: mysqli или PDO?

Что ж, ответ будет таким:

  • Если вы понимаете необходимость использования уровня абстракции базы данных и ищете API для его создания, mysqli - очень хороший выбор, поскольку он действительно поддерживает многие специфичные для mysql функции.
  • Если, как и подавляющее большинство разработчиков PHP, вы используете необработанные вызовы API прямо в коде приложения (что по сути является неправильной практикой), PDO - единственный выбор , поскольку это расширение претендует на роль не просто API, а скорее полу-DAL, все еще неполный, но предлагает множество важных функций, две из которых резко отличают PDO от mysqli:

    • В отличие от mysqli, PDO может связывать заполнители по значению , что делает динамически построенные запросы выполнимыми без нескольких экранов довольно беспорядочного кода.
    • В отличие от mysqli, PDO всегда может вернуть результат запроса в виде простого обычного массива, тогда как mysqli может это сделать только на установках mysqlnd.

Итак, если вы средний пользователь PHP и хотите избавить себя от множества головных болей при использовании собственных подготовленных операторов, PDO - опять же - единственный выбор.
Однако PDO - тоже не серебряная пуля, и у нее есть свои трудности.
Итак, я написал решения для всех распространенных ошибок и сложных случаев в вики-странице тегов PDO.

Тем не менее, все, кто говорит о расширениях, всегда упускают из виду 2 важных факта о Mysqli и PDO:

  1. Подготовленное заявление - не серебряная пуля . Есть динамические идентификаторы, которые нельзя связать с помощью подготовленных операторов. Существуют динамические запросы с неизвестным количеством параметров, что затрудняет построение запроса.

  2. Ни mysqli_ *, ни функции PDO не должны были появляться в коде приложения. Между ними и кодом приложения
    должен быть уровень абстракции , который будет выполнять всю грязную работу по привязке, циклу, обработке ошибок и т. Д. Внутри, делая код приложения СУХИМ и чистым. Особенно для сложных случаев, таких как динамическое построение запросов.

Итак, просто переключиться на PDO или mysqli недостаточно. Нужно использовать ORM, построитель запросов или любой другой класс абстракции базы данных вместо вызова необработанных функций API в их коде.
И наоборот - если у вас есть уровень абстракции между кодом вашего приложения и mysql API - на самом деле не имеет значения, какой движок используется. Вы можете использовать mysql ext до тех пор, пока он не станет устаревшим, а затем легко переписать свой класс абстракции для другого движка, сохранив весь код приложения без изменений.

Вот несколько примеров, основанных на моем классе safemysql, чтобы показать, каким должен быть такой класс абстракции:

$city_ids = array(1,2,3);
$cities = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Сравните эту единственную строку с объемом кода, который вам понадобится с PDO .
Затем сравните с сумасшедшим количеством кода, который вам понадобится, с необработанными подготовленными операторами Mysqli. Обратите внимание, что обработка ошибок, профилирование и ведение журнала запросов уже встроены и работают.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Сравните это с обычными вставками PDO, когда каждое отдельное имя поля повторяется от шести до десяти раз - во всех этих многочисленных именованных заполнителях, привязках и определениях запросов.

Другой пример:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Вы вряд ли найдете пример для PDO для такого практического случая.
И это будет слишком многословно и, скорее всего, небезопасно.

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

98 Trott Oct 12 2012 at 20:23

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

При использовании mysql_*функций вы должны не забывать запускать параметры, задаваемые пользователем mysql_real_escape_string(). Если вы забудете только в одном месте или ускользнете только от части ввода, ваша база данных может подвергнуться атаке.

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

77 enhzflep Oct 12 2012 at 20:24

Потому что (среди прочего) гораздо сложнее обеспечить дезинфекцию входных данных. Если вы используете параметризованные запросы, как это делается с PDO или mysqli, вы можете полностью избежать риска.

Например, кто-то может использовать "enhzflep); drop table users"в качестве имени пользователя. Старые функции позволяют выполнять несколько операторов для каждого запроса, так что что-то вроде этого мерзкого баггера может удалить всю таблицу.

Если бы кто-то использовал PDO mysqli, имя пользователя в конечном итоге было бы "enhzflep); drop table users".

См. Bobby-tables.com .

66 Fluffeh Sep 18 2013 at 19:28

Этот ответ написан, чтобы показать, насколько тривиально обойти плохо написанный код проверки пользователя PHP, как (и с помощью чего) работают эти атаки и как заменить старые функции MySQL безопасным подготовленным оператором - и, в основном, почему пользователи StackOverflow (вероятно, с большим количеством представителей) лают на новых пользователей, которые задают вопросы, чтобы улучшить свой код.

Во-первых, не стесняйтесь создавать эту тестовую базу данных mysql (я назвал свою подготовку):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

После этого мы можем перейти к нашему PHP-коду.

Предположим, следующий сценарий - это процесс проверки для администратора на веб-сайте (упрощенный, но работающий, если вы скопируете и используете его для тестирования):

<?php 

    if(!empty($_POST['user'])) { $user=$_POST['user']; } else { $user='bob';
    }
    if(!empty($_POST['pass'])) { $pass=$_POST['pass']; } else { $pass='bob';
    }

    $database='prep'; $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database"); $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>"; $result=mysql_query($sql); $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>"; $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin) { echo "The check passed. We have a verified admin!<br>"; } else { echo "You could not be verified. Please try again...<br>"; } mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

На первый взгляд кажется вполне законным.

Пользователь ведь должен ввести логин и пароль?

Молодец, не вписывайтесь в следующее:

user: bob
pass: somePass

и отправьте его.

Результат выглядит следующим образом:

You could not be verified. Please try again...

Супер! Работая, как и ожидалось, теперь давайте попробуем фактическое имя пользователя и пароль:

user: Fluffeh
pass: mypass

Удивительный! Всем привет, код корректно проверил админ. Это идеально!

Ну не совсем. Допустим, пользователь - маленький умный человек. Допустим, это я.

Введите следующее:

user: bob
pass: n' or 1=1 or 'm=m

И результат:

The check passed. We have a verified admin!

Поздравляю, вы только что разрешили мне войти в ваш суперзащищенный раздел только для администраторов, где я ввел ложное имя пользователя и ложный пароль. Серьезно, если вы мне не верите, создайте базу данных с кодом, который я предоставил, и запустите этот PHP-код, который на первый взгляд ДЕЙСТВИТЕЛЬНО, кажется, действительно довольно хорошо проверяет имя пользователя и пароль.

Итак, в ответ, ВОТ ПОЧЕМУ ВАС КРИЧУТ.

Итак, давайте посмотрим, что пошло не так, и почему я только что попал в вашу пещеру только для суперадминистраторов. Я сделал предположение и предположил, что вы не были осторожны со своими входными данными, и просто передали их в базу данных напрямую. Я построил ввод таким образом, чтобы ИЗМЕНИТЬ запрос, который вы действительно выполняли. Итак, что это должно было быть и чем оно закончилось?

select id, userid, pass from users where userid='$user' and pass='$pass'

Это запрос, но когда мы заменяем переменные фактическими входными данными, которые мы использовали, мы получаем следующее:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

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

Однако сейчас речь идет не о том, чтобы люди кричали на вас, а о том, чтобы показать вам, как сделать ваш код более безопасным.

Итак, что пошло не так, и как мы можем это исправить?

Это классическая атака с использованием SQL-инъекций. Один из самых простых в этом отношении. По масштабам векторов атаки это малыш, атакующий танк - и побеждающий.

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

Теперь у вас есть лучшие варианты использования mysqli_ или PDO . Я лично большой поклонник PDO, поэтому в остальной части этого ответа я буду использовать PDO. Есть плюсы и минусы, но лично я считаю, что плюсы намного перевешивают минусы. Он переносится на несколько движков баз данных - используете ли вы MySQL или Oracle или что-нибудь еще, - просто изменив строку подключения, у него есть все причудливые функции, которые мы хотим использовать, и он приятный и чистый. Мне нравится чистота.

Теперь давайте снова посмотрим на этот код, на этот раз написанный с использованием объекта PDO:

<?php 

    if(!empty($_POST['user'])) { $user=$_POST['user']; } else { $user='bob';
    }
    if(!empty($_POST['pass'])) { $pass=$_POST['pass']; } else { $pass='bob';
    }
    $isAdmin=false; $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example'); $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)); if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>"; $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Основные отличия в том, что больше нет mysql_*функций. Все это делается с помощью объекта PDO, во-вторых, с помощью подготовленного оператора. А что вы спросите? Это способ сообщить базе данных перед запуском запроса, какой запрос мы собираемся выполнить. В этом случае мы говорим базе данных: «Привет, я собираюсь запустить оператор выбора, требующий идентификатора, идентификатора пользователя и передать от пользователей таблицы, где идентификатор пользователя является переменной, а проход также является переменной».

Затем в операторе execute мы передаем базе данных массив со всеми ожидаемыми переменными.

Результаты фантастические. Давайте попробуем эти комбинации имени пользователя и пароля еще раз:

user: bob
pass: somePass

Пользователь не подтвержден. Потрясающие.

Как насчет:

user: Fluffeh
pass: mypass

Ой, я просто немного волновался, это сработало: проверка прошла. У нас есть проверенный админ!

Теперь давайте попробуем данные, которые умный парень мог бы ввести, чтобы попытаться обойти нашу небольшую систему проверки:

user: bob
pass: n' or 1=1 or 'm=m

На этот раз мы получаем следующее:

You could not be verified. Please try again...

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

Наконец, это не означает, что это ИДЕАЛЬНЫЙ код. Есть еще много вещей, которые вы могли бы сделать для его улучшения, например, использовать хешированные пароли, гарантировать, что, когда вы храните важную информацию в базе данных, вы не храните ее в виде простого текста, имеете несколько уровней проверки - но на самом деле, если вы просто измените свой старый код, подверженный инъекциям, на это, вы будете ХОРОШО на пути к написанию хорошего кода - и тот факт, что вы зашли так далеко и все еще читаете, дает мне чувство надежды, что вы не только реализуете этот тип кода при написании веб-сайтов и приложений, но вы могли бы пойти и изучить те другие вещи, которые я только что упомянул, и многое другое. Пишите лучший код, который вы можете, а не самый простой код, который практически не работает.

34 Alexander Sep 02 2015 at 14:20

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

  • MySQLi - это «улучшенное» расширение для работы с базами данных MySQL. Он использует преимущества функций, доступных в более новых версиях сервера MySQL, предоставляет разработчику как функционально-ориентированный, так и объектно-ориентированный интерфейс, а также делает несколько других отличных вещей.

  • PDO предлагает API, который объединяет большую часть функциональности, которая ранее была распространена среди основных расширений доступа к базам данных, то есть MySQL, PostgreSQL, SQLite, MSSQL и т. Д. Интерфейс предоставляет программисту высокоуровневые объекты для работы с подключениями к базе данных, запросами и т. Д. наборы результатов и низкоуровневые драйверы обеспечивают связь и обработку ресурсов с сервером базы данных. В PDO ведется много дискуссий и работы, и он считается подходящим методом работы с базами данных в современном профессиональном коде.

22 AniMenon Sep 07 2016 at 22:06

Я считаю приведенные выше ответы очень длинными, поэтому резюмирую:

Расширение mysqli имеет ряд преимуществ, основные улучшения по сравнению с расширением mysql:

  • Объектно-ориентированный интерфейс
  • Поддержка подготовленных отчетов
  • Поддержка нескольких утверждений
  • Поддержка транзакций
  • Расширенные возможности отладки
  • Поддержка встроенного сервера

Источник: обзор MySQLi


Как объяснено в приведенных выше ответах, альтернативами mysql являются mysqli и PDO (объекты данных PHP).

  • API поддерживает подготовленные операторы на стороне сервера: поддерживается MYSQLi и PDO
  • API поддерживает подготовленные операторы на стороне клиента: поддерживается только PDO
  • API поддерживает хранимые процедуры: MySQLi и PDO
  • API поддерживает множественные операторы и все функции MySQL 4.1+ - поддерживается MySQLi и в основном также PDO

И MySQLi, и PDO были представлены в PHP 5.0, тогда как MySQL был представлен до PHP 3.0. Следует отметить, что MySQL включен в PHP5.x, хотя в более поздних версиях не рекомендуется.

7 PavelTzonkov Jun 09 2017 at 13:24

С mysql_*помощью mysqli или PDO можно определить почти все функции. Просто включите их поверх своего старого PHP-приложения, и оно будет работать на PHP7. Мое решение здесь .

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null; function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link; } function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK]; } function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass); } function mysql_select_db($db, $link=null) { $link = mysql_link($link); return mysqli_select_db($link, $db); } function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link); } function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link); } function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link); } function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link); } function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link); } function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link); } function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link); } function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link); } function mysql_escape_string($string) {
    return mysql_real_escape_string($string); } function mysql_real_escape_string($string, $link=null) { $link = mysql_link($link); return mysqli_real_escape_string($link, $string); } function mysql_query($sql, $link=null) { $link = mysql_link($link); return mysqli_query($link, $sql); } function mysql_unbuffered_query($sql, $link=null) { $link = mysql_link($link); return mysqli_query($link, $sql, MYSQLI_USE_RESULT); } function mysql_set_charset($charset, $link=null){ $link = mysql_link($link); return mysqli_set_charset($link, $charset); } function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link); } function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link); } function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link); } function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link); } function mysql_get_client_info() { $link = mysql_link();
    return mysqli_get_client_info($link); } function mysql_create_db($db, $link=null) { $link = mysql_link($link); $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db)); return mysqli_query($link, "DROP DATABASE `$db`"); } function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES"); } function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db)); $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`"); } function mysql_list_tables($db, $link=null) { $link = mysql_link($link); $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) { $link = mysql_link($link); mysqli_select_db($link, $db); return mysqli_query($link, $sql); } function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink); } function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink); } function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) { return mysqli_fetch_array($qlink, $result); } function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink); } function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink); } function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink); } function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink); } function mysql_data_seek($qlink, $row) { return mysqli_data_seek($qlink, $row); } function mysql_field_seek($qlink, $offset) { return mysqli_field_seek($qlink, $offset); } function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null) ? mysqli_fetch_object($qlink, $class) : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') { mysqli_data_seek($qlink, $row); $db = mysqli_fetch_assoc($qlink); return $db[$field]; } function mysql_fetch_field($qlink, $offset=null) { if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink); } function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null) mysqli_field_seek($qlink, $offset); $row = mysqli_fetch_array($qlink); return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset); return is_object($field) ? $field->length : false; } function mysql_field_name($qlink, $offset) { $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field)) return false; return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset); if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable; } function mysql_field_type($qlink, $offset) { $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) { try { mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}