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

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

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

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

См. Красную рамку?

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

Зачем


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

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

Причина не использования функции 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 против MySQLi: что вы должны использовать? by Dejan Marjanovic поможет вам выбрать.

И лучшим способом является PDO, и теперь я пишу простой учебник PDO.


Простой и короткий учебник PDO


Вопрос. Первый вопрос, на мой взгляд, был: что такое "PDO"?

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

alt text


Подключение к 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 источника базы данных, в основном принимает четыре параметра, которые являются DSN (имя источника данных) и, возможно, username, password.

Здесь я думаю, что вы знакомы со всеми, кроме DSN; это новое в PDO. 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 метода 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 можно поймать и обработать изящно, если вы решите это сделать.

Хорошо читал:

Подобно:

$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? Не беспокойтесь, здесь мы идем:


Выбор данных

PDO select image

Итак, что вы делаете в 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(), метод дескриптора вашего оператора. Прежде чем вызывать выборку, лучшим подходом будет указание PDO, как вы хотели бы получать данные. В следующем разделе я объясняю это.

Режимы извлечения

Обратите внимание на использование PDO::FETCH_ASSOC в приведенных выше 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();

Вставка и обновление или удаление операторов

Insert and update PDO image

Что мы делаем в функции 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)

Вы также можете связывать с помощью массива execute:

<?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);

Q. Итак, что такое неназванные заполнители и как их использовать?
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-инъекций? by 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 /*"));

Ответ 3

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

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

Пропустите это, предложение по предложению и объясните:

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

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

    NEW! - ext/mysql теперь официально устарел с PHP 5.5!

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

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

    Расширение

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

    Для получения дополнительной информации см. Как я могу предотвратить SQL-инъекцию в PHP?

  • См. Красную рамку?

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

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

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

    /li >

Ответ 4

Простота использования

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

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

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

Переписывание большей базы кода одновременно требует времени. Raison d'être для этой промежуточной альтернативы:

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

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

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

  • Удалите префикс функции 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()
    • и т.д.


  • Ваш код будет работать одинаково и по-прежнему в основном выглядит одинаково:

    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]";
    }
    

Et voilà.
В вашем коде используется PDO.
Теперь настало время использовать его.

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

78Ddi.jpg

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

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

PeuHl.png

Переместите переменные из строки 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_ и экранирования.

Исправить или удалить любую программу oldschool 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 и ее параметризованным операторам.

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

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

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

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

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

niZ7j.png

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

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

M7PXp.png

В то время как в этом ответе подчеркивается, что вы должны заботиться о преимуществах читаемости при удалении 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_.

Просто переключитесь на doesn' t довольно отрезал его. pdo_query() также является интерфейсом на нем.

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

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

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

Ответ 5

Функции mysql_:

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

Ответ 6

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

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

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

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

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

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

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

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

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

Последний интересный момент. Хотя 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);

voila, все параметризовано и безопасно.

Но хорошо, если вам не нравится красная рамка в руководстве, возникает проблема выбора: 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:

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

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

Итак, просто переключение на PDO или mysqli недостаточно. Нужно использовать ORM, или построитель запросов, или какой-либо класс абстракции базы данных, вместо вызова функций raw 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 для обработки такого практического случая.
И это будет слишком многословно и, скорее всего, небезопасно.

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

Ответ 7

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

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

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

Ответ 8

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

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

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

См. bobby-tables.com.

Ответ 9

Этот ответ написан, чтобы показать, насколько тривиальным является обход плохо написанного 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-коду.

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

<?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, который, на взгляд, действительно ДЕЙСТВИТЕЛЬНО действительно проверяет имя пользователя и пароль.

Итак, в ответ, ПОЧЕМУ ВЫ ЗНАЕТЕ.

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

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 в остальной части этого ответа. Есть pro и con, но лично я нахожу, что pro намного перевешивает con. Он переносится через несколько баз данных - независимо от того, используете ли вы 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, во-вторых, он использует подготовленный оператор. Теперь, какое предваряющее выражение вы спрашиваете? Это способ сообщить базу данных перед запуском запроса, каков запрос, который мы будем запускать. В этом случае мы сообщаем базе данных: "Привет, я собираюсь запустить инструкцию select, требующую id, userid и передать из таблицы пользователей, где userid является переменной, а pass также является переменной".

Затем в инструкции 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...

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

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

Ответ 10

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

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

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

Ответ 11

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

Расширение 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, хотя он устарел в более поздних версиях.

Ответ 12

Можно определить почти все функции 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;
}

Ответ 13

MySQL устарела в PHP 5.5.0 и удалена в PHP 7.0.0. Для большого и старого приложения это трудно найти и заменить каждую функцию.

Мы можем использовать функции MySQL, создавая функцию-обертку для каждого ниже выполняемого кода. Нажмите здесь

Ответ 14

Функции, аналогичные этому типу mysql_connect(), mysql_query(), представляют собой предыдущие функции PHP i.e(PHP 4) и теперь не используются.

Они заменяются на mysqli_connect(), mysqli_query() аналогично в последнем PHP5.

Это причина ошибки.

Ответ 15

Это старый вопрос сегодня (январь 2019 года), но он все еще может быть полезным. Около 7 лет назад я создал табличное отображение функциональности MySQL/MySQLi/PDO. Может быть полезной ссылкой. Это онлайн здесь и воспроизведено ниже. Не стесняйтесь копировать и вставлять HTML.

На практике я обнаружил, что преобразование процедурных функций MySQL в ООП MySQLi - это путь наименьшего сопротивления. Прекрасно, когда два соединения с БД открыты одновременно, и это дало нам некоторую гибкость при работе с преобразованиями - мы могли преобразовывать сценарии по частям, по одному запросу за раз. Хотя я не могу рекомендовать это сегодня, это было целесообразно в то время.

<div class="container">

<h2>Mapping Obsolete MySQL Functions to Current PHP Extensions</h2>
<table>
<tr><th>MySQL Extension</th><th>MySQL<b><i>i</i></b></th><th>PDO</th></tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-affected-rows.php">mysql_affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.affected-rows.php">mysqli::$affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-client-encoding.php">mysql_client_encoding</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-close.php">mysql_close</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.close.php">mysqli::close</a></td>
    <td>Assign NULL to PDO Object</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-connect.php">mysql_connect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-create-db.php">mysql_create_db</a></td>
    <td>Query: CREATE DATABASE</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-data-seek.php">mysql_data_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.data-seek.php">mysqli_stmt::data_seek</a></td>
    <td>PDO::FETCH_ORI_ABS (?)</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-name.php">mysql_db_name</a></td>
    <td>Query: SELECT DATABASE()</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-query.php">mysql_db_query</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-drop-db.php">mysql_drop_db</a></td>
    <td>Query: DROP DATABASE</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-errno.php">mysql_errno</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.errno.php">mysqli::$errno</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorcode.php">PDO::errorCode</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-error.php">mysql_error</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.error-list.php">mysqli::$error_list</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorinfo.php">PDO::errorInfo</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-escape-string.php">mysql_escape_string</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-array.php">mysql_fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-array.php">mysqli_result::fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-assoc.php">mysql_fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-assoc.php">mysqli_result::fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-field.php">mysql_fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field.php">mysqli_result::fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-lengths.php">mysql_fetch_lengths</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.lengths.php">mysqli_result::$lengths</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-object.php">mysql_fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-object.php">mysqli_result::fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-row.php">mysql_fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-row.php">mysqli_result::fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-flags.php">mysql_field_flags</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-fields.php">mysqli_result::fetch_fields</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-len.php">mysql_field_len</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-name.php">mysql_field_name</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-seek.php">mysql_field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.field-seek.php">mysqli_result::field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-table.php">mysql_field_table</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-type.php">mysql_field_type</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-free-result.php">mysql_free_result</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.free.php">mysqli_result::free</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.closecursor.php">PDOStatement::closeCursor</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-client-info.php">mysql_get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-client-info.php">mysqli::get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-host-info.php">mysql_get_host_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-host-info.php">mysqli::$host_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-proto-info.php">mysql_get_proto_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-proto-info.php">mysqli::$protocol_version</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-server-info.php">mysql_get_server_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-server-info.php">mysqli::$server_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-info.php">mysql_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.info.php">mysqli::$info</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-insert-id.php">mysql_insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.insert-id.php">mysqli::$insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.lastinsertid.php">PDO::lastInsertId</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-dbs.php">mysql_list_dbs</a></td>
    <td>Query: SHOW DATABASES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-fields.php">mysql_list_fields</a></td>
    <td>Query: SHOW COLUMNS</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-processes.php">mysql_list_processes</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-tables.php">mysql_list_tables</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-fields.php">mysql_num_fields</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.field-count.php">mysqli::$field_count</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.columncount.php">PDOStatement::columnCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-rows.php">mysql_num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.num-rows.php">mysqli_stmt::$num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-pconnect.php">mysql_pconnect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-ping.php">mysql_ping</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.ping.php">mysqli::ping</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-query.php">mysql_query</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.query.php">mysqli::query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.query.php">PDO::query</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-real-escape-string.php">mysql_real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.real-escape-string.php">mysqli::real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.quote.php">PDO::quote</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-result.php">mysql_result</a></td>
    <td>Combination</td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetchcolumn.php">PDOStatement::fetchColumn</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-select-db.php">mysql_select_db</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.send-query.php">mysqli::send_query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-set-charset.php">mysql_set_charset</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-stat.php">mysql_stat</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.stat.php">mysqli::stat</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-tablename.php">mysql_tablename</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-thread-id.php">mysql_thread_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-unbuffered-query.php">mysql_unbuffered_query</a></td>
    <td>See <a href="http://www.php.net/manual/en/mysqlinfo.concepts.buffering.php">Buffering Concepts</a></td>
    <td> </td>
    </tr>
</table>

</div><!-- container -->

Ответ 16

Функции mysql_ * устарели (начиная с PHP 5.5), учитывая тот факт, что были разработаны более совершенные функции и структуры кода. Тот факт, что функция устарела, означает, что больше не нужно прилагать усилий для ее улучшения с точки зрения производительности и безопасности, что означает, что она менее надежна в будущем.

Если вам нужно больше причин:

  • Функции mysql_ * не поддерживают подготовленные операторы.
  • Функции mysql_ * не поддерживают привязку параметров.
  • В функциях mysql_ * отсутствует функциональность для объектно-ориентированного программирования.
  • список можно продолжить...