web-dev-qa-db-fra.com

Pourquoi ne devrais-je pas utiliser les fonctions mysql_ * en PHP?

Quelles sont les raisons techniques pour lesquelles on ne devrait pas utiliser les fonctions mysql_*? (par exemple, mysql_query(), mysql_connect() ou mysql_real_escape_string())?

Pourquoi devrais-je utiliser autre chose même s'ils fonctionnent sur mon site?

S'ils ne fonctionnent pas sur mon site, pourquoi des erreurs telles que 

Avertissement: mysql_connect (): Aucun fichier ou répertoire de ce type

2308
Madara Uchiha

L'extension MySQL:

  • N'est pas en développement actif
  • Est officiellement obsolète à partir de PHP 5.5 (publié en juin 2013).
  • A été supprimé entièrement à partir de PHP 7.0 (publié en décembre 2015)
    • Cela signifie qu'à compter du 31 décembre 2018 il n'existera plus dans aucune version prise en charge de PHP. Actuellement, il ne reçoit que sécurité mises à jour.
  • Manque d'une interface OO
  • Ne supporte pas:
    • Requêtes asynchrones non bloquantes
    • Instructions préparées ou requêtes paramétrées
    • Procédures stockées
    • Déclarations multiples
    • Transactions
    • La "nouvelle" méthode d'authentification par mot de passe (activée par défaut dans MySQL 5.6; obligatoire dans 5.7)
    • Toutes les fonctionnalités de MySQL 5.1

Étant donné qu'il est déconseillé, son utilisation rend votre code moins fiable. 

L'absence de prise en charge des instructions préparées est particulièrement importante car elles fournissent une méthode plus claire et moins sujette aux erreurs pour échapper et citer des données externes que de l'échapper manuellement avec un appel de fonction séparé.

Voir la comparaison des extensions SQL .

1934
Quentin

PHP propose trois API différentes pour se connecter à MySQL. Ce sont les mysql (supprimés à partir de PHP 7), mysqli , et PDO extensions.

Les fonctions _mysql_*_ étaient très populaires, mais leur utilisation n’est plus encouragée. L’équipe de documentation discute de la situation en matière de sécurité de la base de données et apprend aux utilisateurs à s’éloigner de l’extension ext/mysql couramment utilisée en fait partie (vérifier php.internals: mysql).

Et l’équipe de développeurs PHP a pris la décision de générer E_DEPRECATED erreurs lorsque les utilisateurs se connectent à MySQL, que ce soit via mysql_connect(), mysql_pconnect() ou la fonctionnalité de connexion implicite intégrée à _ext/mysql_.

ext/mysql était officiellement obsolète à partir de PHP 5.5 et a été supprimé à partir de PHP 7 .

Voir la boîte rouge?

Lorsque vous accédez à une page de manuel de fonction _mysql_*_, vous voyez un encadré rouge expliquant qu’elle ne doit plus être utilisée.

Pourquoi


S'éloigner de _ext/mysql_ ne concerne pas seulement la sécurité, mais aussi l'accès à toutes les fonctionnalités de la base de données MySQL.

_ext/mysql_ a été construit pour MySQL 3.23 et n’a reçu que très peu d’ajouts depuis lors, tout en maintenant la compatibilité avec cette ancienne version qui rend le code un peu plus difficile à maintenir. Les fonctions manquantes non prises en charge par _ext/mysql_ incluent: (de PHP manual).

Raison de ne pas utiliser la fonction _mysql_*_ :

  • Pas en développement actif
  • Supprimé à partir de PHP 7
  • Manque d'une interface OO
  • Ne supporte pas les requêtes asynchrones non bloquantes
  • Ne supporte pas les instructions préparées ou requêtes paramétrées
  • Ne supporte pas les procédures stockées
  • Ne supporte pas les déclarations multiples
  • Ne supporte pas transactions
  • Ne supporte pas toutes les fonctionnalités de MySQL 5.1

Point cité dans la réponse de Quentin

Le manque de prise en charge des instructions préparées est particulièrement important car elles fournissent une méthode plus claire et moins sujette aux erreurs pour échapper et citer des données externes que de l'échapper manuellement avec un appel de fonction séparé.

Voir la comparaison des extensions SQL .


Suppression des avertissements de dépréciation

Pendant que le code est converti en MySQLi/PDO, _E_DEPRECATED_ erreurs peuvent être supprimées en définissant _error_reporting_ dans php.ini pour exclure _E_DEPRECATED:_

_error_reporting = E_ALL ^ E_DEPRECATED
_

Notez que cela masquera également d'autres avertissements de dépréciation , qui peuvent toutefois être destinés à des applications autres que MySQL. (de PHP manual)

L'article PDO vs MySQLi: lequel devriez-vous utiliser? by Dejan Marjanovic vous aidera à choisir.

Et un meilleur moyen est PDO, et j’écris maintenant un simple tutoriel PDO.


Un tutoriel PDO simple et rapide


Q. La première question que je me posais était: qu'est-ce que "PDO"?

A. " PDO - PHP Objets de données - est une couche d'accès à une base de données fournissant une méthode uniforme d'accès à plusieurs bases de données."

alt text


Connexion à MySQL

Avec la fonction _mysql_*_ ou on peut le dire à l’ancienne (obsolète dans PHP 5.5 et supérieur)

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

Avec PDO: Tout ce que vous avez à faire est de créer un nouvel objet PDO. Le constructeur accepte les paramètres permettant de spécifier la source de la base de données. Le constructeur de PDO utilise principalement quatre paramètres qui sont DSN (nom de la source de données) et éventuellement username, password.

Ici, je pense que vous connaissez tout sauf DSN; c'est nouveau dans PDO. DSN est en gros une chaîne d'options qui indique à PDO le pilote à utiliser et les détails de la connexion. Pour plus de références, consultez PDO MySQL DSN .

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

Remarque: vous pouvez également utiliser _charset=UTF-8_, mais cela entraîne parfois une erreur. Il est donc préférable d'utiliser _utf8_.

S'il y a une erreur de connexion, un objet PDOException peut être intercepté pour pouvoir manipuler plus tard Exception.

Bonne lecture : Connexions et gestion des connexions ¶

Vous pouvez également transmettre plusieurs options de pilote sous forme de tableau au quatrième paramètre. Je recommande de passer le paramètre qui met PDO en mode exception. Étant donné que certains pilotes PDO ne prennent pas en charge les instructions préparées natives, PDO exécute l’émulation de la préparation. Il vous permet également d'activer manuellement cette émulation. Pour utiliser les instructions préparées natives côté serveur, vous devez explicitement le définir false.

L'autre consiste à désactiver l'émulation de préparation qui est activée par défaut dans le pilote MySQL, mais l'émulation de préparation doit être désactivée pour pouvoir utiliser PDO en toute sécurité.

J'expliquerai plus tard pourquoi l’émulation de préparation devrait être désactivée. Pour trouver la raison, veuillez vérifier this post .

Il n'est utilisable que si vous utilisez une ancienne version de MySQL que je ne recommande pas.

Voici un exemple de la façon dont vous pouvez le faire:

_$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));
_

Peut-on définir des attributs après la construction d'un PDO?

Oui , nous pouvons également définir certains attributs après la construction du PDO avec la méthode 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);
_

La gestion des erreurs


Le traitement des erreurs est beaucoup plus facile dans PDO que _mysql_*_.

Une pratique courante lors de l'utilisation de _mysql_*_ est la suivante:

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

OR die() n'est pas un bon moyen de gérer l'erreur car nous ne pouvons pas gérer la chose dans die. Il ne fera que mettre fin brusquement au script, puis faire écho à l’erreur que vous ne souhaitez généralement PAS montrer à vos utilisateurs finaux et laisser les pirates sanglants découvrir votre schéma. Alternativement, les valeurs de retour des fonctions _mysql_*_ peuvent souvent être utilisées avec mysql_error () pour gérer les erreurs.

PDO offre une meilleure solution: les exceptions. Tout ce que nous faisons avec PDO doit être emballé dans un bloc try-catch. Nous pouvons forcer PDO dans l'un des trois modes d'erreur en définissant l'attribut mode d'erreur. Trois modes de traitement des erreurs sont ci-dessous.

  • _PDO::ERRMODE_SILENT_. Il ne fait que définir des codes d'erreur et agit quasiment de la même façon que _mysql_*_ où vous devez vérifier chaque résultat, puis consulter $db->errorInfo(); pour obtenir les détails de l'erreur.
  • _PDO::ERRMODE_WARNING_ Soulever _E_WARNING_. (Avertissements d'exécution (erreurs non fatales). L'exécution du script n'est pas arrêtée.)
  • _PDO::ERRMODE_EXCEPTION_: Lance des exceptions. Cela représente une erreur soulevée par PDO. Vous ne devriez pas jeter un PDOException à partir de votre propre code. Voir Exceptions pour plus d'informations sur les exceptions en PHP. Cela ressemble beaucoup à or die(mysql_error());, quand il n'est pas intercepté. Mais contrairement à or die(), PDOException peut être intercepté et traité avec élégance si vous choisissez de le faire.

Bonne lecture :

Comme:

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

Et vous pouvez l'envelopper dans try-catch, comme ci-dessous:

_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());
}
_

Vous n'êtes pas obligé de manipuler avec try-catch pour le moment. Vous pouvez le prendre à tout moment, mais je vous recommande fortement d'utiliser try-catch. En outre, il peut être plus logique de le capturer en dehors de la fonction qui appelle le truc 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.
}
_

Aussi, vous pouvez manipuler par or die() ou nous pouvons dire comme _mysql_*_, mais ce sera vraiment varié. Vous pouvez masquer les messages d'erreur dangereux en production en tournant _display_errors off_ et en lisant simplement votre journal des erreurs.

Maintenant, après avoir lu tout ce qui précède, vous vous dites probablement: qu’est-ce que c’est que lorsque je veux commencer à apprendre des déclarations simples SELECT, INSERT, UPDATE ou DELETE? Ne t'inquiète pas, c'est parti:


Sélection des données

PDO select image

Donc, ce que vous faites dans _mysql_*_ est:

_<?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'];
}
_

Maintenant, dans PDO, vous pouvez faire ceci comme:

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

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

Ou

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

//Use $results
_

Remarque : Si vous utilisez la méthode comme ci-dessous (query()), cette méthode renvoie un objet PDOStatement. Donc, si vous voulez récupérer le résultat, utilisez-le comme ci-dessus.

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

Dans PDO Data, il est obtenu via la méthode ->fetch(), de votre descripteur d'instruction. Avant d'appeler l'extraction, la meilleure approche serait d'indiquer à PDO comment vous souhaitez que les données soient extraites. J'explique ceci dans la section ci-dessous.

Modes de récupération

Notez l'utilisation de _PDO::FETCH_ASSOC_ dans les codes fetch() et fetchAll() ci-dessus. Ceci indique à PDO de renvoyer les lignes sous forme de tableau associatif avec les noms de champs sous forme de clés. Il existe de nombreux autres modes d'extraction que je vais expliquer un à un.

Tout d’abord, j’explique comment sélectionner le mode de récupération:

_ $stmt->fetch(PDO::FETCH_ASSOC)
_

Dans ce qui précède, j'ai utilisé fetch(). Vous pouvez aussi utiliser:

J'arrive maintenant au mode de récupération:

  • _PDO::FETCH_ASSOC_: renvoie un tableau indexé par nom de colonne tel qu'il est renvoyé dans votre jeu de résultats
  • _PDO::FETCH_BOTH_ (par défaut): renvoie un tableau indexé à la fois par le nom de la colonne et par le numéro de la colonne à l'index 0, tel qu'il est renvoyé dans votre jeu de résultats

Il y a encore plus de choix! Lisez-les tous dans PDOStatement Récupérer de la documentation. .

Obtenir le nombre de lignes :

Au lieu d'utiliser _mysql_num_rows_ pour obtenir le nombre de lignes renvoyées, vous pouvez obtenir un PDOStatement et faire rowCount(), comme suit:

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

Obtenir le dernier identifiant inséré

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

Insérer et mettre à jour ou supprimer des déclarations

Insert and update PDO image

Ce que nous faisons dans la fonction _mysql_*_ est:

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

Et dans pdo, cette même chose peut être faite par:

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

Dans la requête ci-dessus PDO::exec exécuter une instruction SQL et renvoie le nombre de lignes affectées.

Insérer et supprimer seront couverts plus tard.

La méthode ci-dessus n'est utile que lorsque vous n'utilisez pas de variable dans une requête. Mais lorsque vous devez utiliser une variable dans une requête, n’essayez jamais de faire comme ci-dessus et là pour une instruction préparée ou une instruction paramétrée est.


Déclarations préparées

Q. Qu'est-ce qu'une déclaration préparée et pourquoi en ai-je besoin?
A. Une instruction préparée est une instruction SQL précompilée qui peut être exécutée plusieurs fois en envoyant uniquement les données au serveur.

Le flux de travail typique de l’utilisation d’une instruction préparée est le suivant ( extrait de Wikipedia trois 3 points ):

  1. Prepare : le modèle d'instruction est créé par l'application et envoyé au système de gestion de base de données (SGBD). Certaines valeurs restent non spécifiées, appelées paramètres, espaces réservés ou variables de liaison (étiquetées _?_ ci-dessous):

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

  2. Le SGBD analyse, compile, optimise les requêtes sur le modèle d'instruction et stocke le résultat sans l'exécuter.

  3. Execute : plus tard, l'application fournit (ou lie) des valeurs pour les paramètres et le SGBD exécute l'instruction (renvoyant éventuellement un résultat). L'application peut exécuter l'instruction autant de fois qu'elle le souhaite avec des valeurs différentes. Dans cet exemple, il peut fournir "Pain" pour le premier paramètre et _1.00_ pour le deuxième paramètre.

Vous pouvez utiliser une instruction préparée en incluant des espaces réservés dans votre code SQL. Il existe essentiellement trois unités sans espaces réservés (n'essayez pas cela avec la variable supérieure à une), une avec des espaces réservés non nommés et une avec des espaces réservés nommés.

Q. Alors maintenant, quels sont les espaces réservés nommés et comment puis-je les utiliser?
A. Espaces réservés nommés. Utilisez des noms descriptifs précédés de deux points, au lieu de points d'interrogation. Nous ne nous soucions pas de la position/ordre de la valeur dans le nom titulaire:

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

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

Vous pouvez également lier en utilisant un tableau 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);
_

Une autre fonctionnalité intéressante pour les amis OOP est que les espaces réservés nommés ont la possibilité d'insérer des objets directement dans votre base de données, à condition que les propriétés correspondent aux champs nommés. Par exemple:

_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. Alors maintenant, quels sont les espaces réservés non nommés et comment puis-je les utiliser?
A ( Prenons un exemple:

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

et

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

Dans ce qui précède, vous pouvez voir ces _?_ à la place d'un nom comme dans un pseudo-nom. Dans le premier exemple, nous affectons des variables aux différents espaces réservés ($stmt->bindValue(1, $name, PDO::PARAM_STR);). Ensuite, nous affectons des valeurs à ces espaces réservés et exécutons l'instruction. Dans le deuxième exemple, le premier élément du tableau va au premier _?_ et au deuxième au deuxième _?_.

NOTE: Dans espaces réservés non nommés , nous devons prendre soin de la bonne ordre des éléments du tableau que nous transmettons à la méthode PDOStatement::execute().


SELECT, INSERT, UPDATE, DELETE requêtes préparées

  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();
    _

NOTE:

Cependant, PDO et/ou MySQLi ne sont pas complètement sûrs. Vérifiez la réponse Les instructions préparées par PDO sont-elles suffisantes pour empêcher une injection SQL? de ircmaxell . Aussi, je cite une partie de sa réponse:

_$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 /*"));
_
1243
NullPoiиteя

Commençons par le commentaire standard que nous donnons à tout le monde: 

S'il vous plaît, n'utilisez pas les fonctions mysql_* dans le nouveau code . Ils ne sont plus maintenus et sont officiellement obsolètes . Voir la boîte rouge ? En savoir plus sur instructions préparées et utiliser PDO ou MySQLi - cet article vous aidera à choisir. Si vous choisissez PDO, voici un bon tutoriel _.

Passons en revue, phrase par phrase, et expliquons:

  • Ils ne sont plus maintenus et sont officiellement obsolètes

    Cela signifie que la communauté PHP supprime progressivement le support de ces fonctions très anciennes. Ils sont susceptibles de ne pas exister dans une future version (récente) de PHP! Si vous continuez à utiliser ces fonctions, votre code risque d’être endommagé dans un avenir pas si lointain.

    NOUVEAU! - ext/mysql est maintenant officiellement déconseillé à partir de PHP 5.5! } [

    Plus récent! ext/mysql A &EACUTE;T&EACUTE; SUPPRIM&EACUTE; DANS PHP 7.

  • Au lieu de cela, vous devriez apprendre des déclarations préparées

    L'extension mysql_* ne prend pas en charge les instructions prepare, ce qui constitue (entre autres) une contre-mesure très efficace contre SQL Injection. Cela corrigeait une très sérieuse vulnérabilité dans les applications dépendantes de MySQL qui permettait aux attaquants d'accéder à votre script et d'effectuer toute requête possible sur votre base de données.

    Pour plus d'informations, voir COMMENT PUIS-JE EMP&ECIRC;CHER L'INJECTION SQL EN PHP?

  • Voir la boîte rouge?

    Lorsque vous accédez à une page de manuel de la fonction mysql, vous voyez un encadré rouge expliquant qu’il ne devrait plus être utilisé.

  • Utilise soit PDO, soit MySQLi

    Il existe des alternatives meilleures, plus robustes et mieux construites, PDO - PHP DATABASE OBJECT }, qui offre une approche complète OOP de l'interaction avec la base de données et MYSQLI, qui est une amélioration spécifique à MySQL.

285
Madara Uchiha

Facilité d'utilisation

Les raisons analytiques et synthétiques ont déjà été mentionnées. Pour les nouveaux arrivants, il existe une incitation plus importante à cesser d’utiliser les fonctions obsolètes de mysql_.

Les API de base de données contemporaines sont juste faciles à utiliser.

Ce sont principalement les paramètres liés qui peuvent simplifier le code. Et avec d'excellents tutoriels (voir ci-dessus) , la transition vers PDO n'est pas trop ardue.

Réécrire une base de code plus grande à la fois prend cependant du temps. Raison d'être pour cette alternative intermédiaire:

Fonctions pdo_ * équivalentes à la place de mysql_ *

En utilisant < pdo_mysql.php > vous pouvez basculer des anciennes fonctions mysql_ avec effort minimal. Il ajoute _pdo__ wrappers de fonctions qui remplacent leurs homologues _mysql__.

  1. Simplement _include_once(_ "pdo_mysql.php" _);_ dans chaque script d'invocation devant interagir avec la base de données.

  2. Retirer le _mysql__ préfixe de la fonction partout ​​et remplacez-le par pdo_.

    • _mysql__connect() devient _pdo__connect()
    • _mysql__query() devient _pdo__query()
    • _mysql__num_rows() devient _pdo__num_rows()
    • _mysql__insert_id() devient _pdo__insert_id()
    • _mysql__fetch_array() devient _pdo__fetch_array()
    • _mysql__fetch_assoc() devient _pdo__fetch_assoc()
    • _mysql__real_escape_string() devient _pdo__real_escape_string()
    • et ainsi de suite ...

  3. Votre code fonctionnera de la même manière et aura toujours le même aspect:

    _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à.
Votre code est tiliser PDO.
Maintenant il est temps de réellement tiliser.

Les paramètres liés peuvent être faciles à utiliser

Vous avez juste besoin d'une API moins lourde.

pdo_query() ajoute un support très facile pour les paramètres liés. La conversion de l'ancien code est simple:

Déplacez vos variables hors de la chaîne SQL.

  • Ajoutez-les en tant que paramètres de fonction délimités par des virgules à pdo_query().
  • Placez les points d'interrogation _?_ comme espaces réservés où les variables étaient antérieures.
  • Supprimez _'_ guillemets simples qui incluaient auparavant des valeurs de chaîne/variables.

L'avantage devient plus évident pour un code plus long.

Souvent, les variables de chaîne ne sont pas simplement interpolées en SQL, mais concaténées avec des appels d'échappement entre les deux.

_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")
_

Avec _?_ espaces réservés appliqués, vous n'avez pas à vous en préoccuper:

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

Rappelez-vous que pdo_ * permet toujours soit o.
N'échappez pas une variable et ​​la lier dans la même requête.

  • La fonctionnalité d'espace réservé est fournie par le véritable PDO derrière elle.
  • Ainsi, nous autorisons également _:named_ listes d'espaces réservés ultérieurement.

Plus important encore, vous pouvez passer les variables $ _REQUEST [] en toute sécurité derrière toute requête. Une fois soumis, les champs _<form>_ correspondent à la structure de la base de données, mais sont même plus courts:

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

Tellement de simplicité. Mais revenons à quelques conseils de réécriture et à des raisons techniques expliquant pourquoi vous voudrez peut-être vous débarrasser de _mysql__ et s'échapper.

Corrigez ou supprimez n'importe quel oldschool sanitize() function

Une fois que vous avez tout converti _mysql__ les appels à _pdo_query_ avec les paramètres liés, supprimez tous les appels redondants _pdo_real_escape_string_.

En particulier, vous devez corriger les fonctions sanitize ou clean ou filterThis ou _clean_data_ annoncées par des tutoriels datés sous une forme ou une autre:

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

Le bogue le plus criant ici est le manque de documentation. Plus important encore, l'ordre de filtrage était exactement dans le mauvais ordre.

  • L'ordre correct aurait été: obsolète stripslashes comme appel le plus à l'intérieur, puis trim, après _strip_tags_, htmlentities pour le contexte de sortie, et enfin le __escape_string_ as son application doit directement précéder l'intersparsing SQL.

  • Mais dans un premier temps, il suffit de se débarrasser de l'appel __real_escape_string_ .

  • Vous devrez peut-être conserver le reste de votre fonction sanitize() pour l'instant si votre flux de base de données et d'application attend des chaînes sécurisées par le contexte HTML. Ajoutez un commentaire pour qu'il n'applique que le HTML s'échappant désormais.

  • La gestion des chaînes/valeurs est déléguée à PDO et à ses instructions paramétrées.

  • S'il est fait mention de stripslashes() dans votre fonction d'assainissement, cela peut indiquer un niveau de supervision supérieur.

    Note historique sur magic_quotes. Cette fonctionnalité est à juste titre déconseillée. Il est souvent décrit à tort comme un échec sécurité fonctionnalité. Mais les magic_quotes sont un élément de sécurité aussi défaillant que les balles de tennis ont échoué en tant que source de nutrition. Ce n'était tout simplement pas leur but.

    L’implémentation originale dans PHP2/FI l’a explicitement introduite avec juste "les guillemets seront automatiquement échappés, ce qui facilitera la transmission des données de formulaire directement aux requêtes msql". En particulier, il était accidentellement sûr d'utiliser avec mSQL , car cela ne supportait que ASCII.
    Puis PHP3/Zend a réintroduit magic_quotes pour MySQL et l’a mal documenté. Mais à l’origine, c’était juste un fonctionnalité , sans intention de sécurité.

Comment les déclarations préparées diffèrent

Lorsque vous incorporez des variables de chaîne dans les requêtes SQL, cela ne devient pas simplement plus complexe à suivre. MySQL s’efforce également de séparer à nouveau le code et les données.

Les injections SQL sont simplement faites quand les données se fondent dans le code le contexte. Un serveur de base de données ne peut pas identifier plus tard où PHP les variables initialement collées entre les clauses de requête.

Avec les paramètres liés, vous séparez le code SQL et les valeurs de contexte SQL dans votre code PHP. Mais cela ne se reproduit plus dans les coulisses (sauf avec PDO :: EMULATE_PREPARES). Votre base de données reçoit les commandes SQL non modifiées et les valeurs de variable 1: 1.

Bien que cette réponse souligne que vous devez vous soucier des avantages de la facilité à déposer des _mysql__. De temps en temps, il existe également un avantage en termes de performances (INSERT répétés avec juste des valeurs différentes) du fait de cette séparation visible et technique des données/codes.

Attention, la liaison de paramètres n'est toujours pas une solution miracle unique contre toutes injections SQL. Il gère l'utilisation la plus courante des données/valeurs. Mais vous ne pouvez pas ajouter des identifiants de nom de table/table à la liste blanche, une aide à la construction de clause dynamique ou simplement des listes de valeurs de tableau.

Utilisation PDO hybride

Ces fonctions d'encapsulation _pdo_*_ constituent une API stop-gap conviviale pour le codage. (C'est à peu près ce que MYSQLI aurait pu être s'il n'y avait pas eu le décalage de signature de fonction idiosyncratique). Ils exposent également le vrai AOP dans la plupart des cas.
La réécriture ne doit pas s'arrêter à l'utilisation des nouveaux noms de fonction pdo_. Vous pouvez passer un à un chaque pdo_query () en un simple appel $ pdo-> prepare () -> execute ().

Cependant, il est préférable de recommencer à simplifier. Par exemple, le résultat de recherche courant:

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

Peut être remplacé par une simple itération foreach:

_foreach ($result as $row) {
_

Ou mieux encore, une récupération directe et complète du tableau:

_$result->fetchAll();
_

Dans la plupart des cas, vous obtiendrez des avertissements plus utiles que PDO ou que mysql_ ne fournit généralement pas après l’échec des requêtes.

Autres options

Donc, cela, espérons-le, visualise quelques pratiques raisons et une voie utile pour abandonner _mysql__.

Passer à pdo ne suffit pas. pdo_query() est également juste une interface sur celui-ci.

Sauf si vous introduisez également la liaison de paramètre ou pouvez utiliser quelque chose d'autre de la plus jolie API, c'est un commutateur inutile. J'espère que cela sera décrit assez simplement pour ne pas décourager davantage les nouveaux arrivants. (L'éducation fonctionne généralement mieux que la prohibition.)

Bien qu'il soit qualifié pour la catégorie des choses les plus simples qui pourraient éventuellement fonctionner, il s'agit également d'un code très expérimental. Je viens de l'écrire au cours du week-end. Il existe cependant une pléthore d'alternatives. Juste Google pour abstraction de base de données PHP et parcourez un peu. Il y a toujours eu et il y aura toujours d'excellentes bibliothèques pour ce type de tâches.

Si vous souhaitez simplifier davantage votre interaction avec la base de données, essayez des mappeurs tels que Paris/Idiorm . Tout comme personne n'utilise plus le DOM fade en JavaScript, vous n'avez pas besoin de garder une interface de base de données brute de nos jours.

212
mario

Les fonctions mysql_:

  1. sont obsolètes - ils ne sont plus maintenus
  2. ne vous permet pas de passer facilement à une autre base de données
  3. ne supporte pas les déclarations préparées, d'où
  4. encourager les programmeurs à utiliser la concaténation pour créer des requêtes, ce qui entraîne des vulnérabilités d'injection SQL
136
Alnitak

En parlant de technique , il n'y en a que quelques-unes, extrêmement spécifiques et rarement utilisées. Très probablement, vous ne les utiliserez jamais dans votre vie.
Peut-être que je suis trop ignorant, mais je n’ai jamais eu l’occasion de les utiliser comme

  • requêtes asynchrones non bloquantes
  • procédures stockées renvoyant plusieurs résultats
  • Cryptage (SSL)
  • Compression

Si vous en avez besoin, ce sont sans aucun doute des raisons techniques pour passer de l'extension mysql à quelque chose de plus élégant et moderne.

Néanmoins, il existe également des problèmes non techniques qui peuvent rendre votre expérience un peu plus difficile

  • toute utilisation ultérieure de ces fonctions avec les versions modernes PHP générera des notifications de niveau déprécié. Ils peuvent simplement être désactivés.
  • dans un futur lointain, ils pourront éventuellement être supprimés de la construction par défaut PHP. Ce n'est pas un problème aussi, car mydsql ext sera transféré dans PECL et chaque hébergeur se fera un plaisir de compiler PHP avec lui, car ils ne veulent pas perdre les clients dont les sites travaillaient depuis des décennies.
  • forte résistance de la communauté Stackoverflow. Vous mentionnez souvent ces fonctions honnêtes, on vous dit qu'elles sont strictement taboues.
  • étant un utilisateur moyen PHP, votre idée d'utiliser ces fonctions est très probablement source d'erreur et erronée. Juste à cause de tous ces nombreux tutoriels et manuels qui vous apprennent mal. Pas les fonctions elles-mêmes - je dois le souligner - mais la façon dont elles sont utilisées.

Ce dernier problème est un problème.
Mais, à mon avis, la solution proposée n’est pas meilleure non plus.
Il me semble trop idéaliste un rêve que tous ces PHP utilisateurs apprendront à gérer correctement les requêtes SQL immediatement. Très probablement, ils changeraient simplement mysql_ * en mysqli_ * mécaniquement, en laissant la même approche . Surtout parce que mysqli rend l'utilisation des déclarations préparées incroyablement douloureuse et gênante.
Sans oublier que native les instructions préparées ne suffisent pas à protéger de SQL injections, et ni mysqli ni PDO ne proposent de solution.

Ainsi, au lieu de lutter contre cette extension honnête, je préférerais lutter contre les mauvaises pratiques et éduquer les gens de la bonne façon.

Il existe également des raisons fausses ou non significatives, telles que

  • Ne supporte pas les procédures stockées (nous utilisions mysql_query("CALL my_proc"); depuis des lustres)
  • Ne supporte pas les transactions (comme ci-dessus)
  • Ne prend pas en charge les instructions multiples (qui en a besoin?)
  • Pas en cours de développement actif (et alors, qu'est-ce que cela affecte vous de manière pratique?)
  • Manque d'une interface OO (pour en créer une, cela prend quelques heures)
  • Ne prend pas en charge les instructions préparées ou les requêtes paramétrées

Le dernier point est intéressant. Bien que mysql ext ne supporte pas les instructions préparées native , elles ne sont pas nécessaires pour la sécurité. Nous pouvons facilement simuler des instructions préparées à l'aide d'espaces réservés gérés manuellement (comme le fait 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 , tout est paramétré et sécurisé.

Mais bon, si vous n'aimez pas la boîte rouge dans le manuel, un problème de choix se pose: mysqli ou PDO?

Eh bien, la réponse serait la suivante:

  • Si vous comprenez la nécessité d'utiliser un couche d'abstraction de base de données et de rechercher une API pour en créer une, mysqli est un très bon choix, car il supporte en effet de nombreuses fonctionnalités spécifiques à mysql.
  • Si, comme la grande majorité des PHP personnes, vous utilisez des appels d'API bruts directement dans le code de l'application (ce qui est fondamentalement une mauvaise pratique) - PDO est le seul choix possible , car cette extension prétend ne pas être simplement une API, mais plutôt un semi-DAL, encore incomplet, mais offre de nombreuses fonctionnalités importantes, dont deux permettent à PDO de se distinguer de façon critique de mysqli:

    • contrairement à mysqli, PDO peut lier des espaces réservés par valeur , ce qui permet de réaliser des requêtes construites de manière dynamique sans plusieurs écrans de code assez encombrant.
    • contrairement à mysqli, PDO peut toujours renvoyer le résultat d'une requête dans un simple tableau habituel, alors que mysqli ne peut le faire que sur des installations mysqlnd.

Donc, si vous êtes un utilisateur moyen PHP et que vous souhaitez vous épargner une tonne de maux de tête lorsque vous utilisez des instructions préparées natives, PDO - là encore - est le seul choix possible.
Cependant, le PDO n’est pas une solution miracle, il a aussi des difficultés.
Ainsi, j’ai écrit des solutions pour tous les pièges courants et les cas complexes dans le wiki du tag PDO

Néanmoins, tout le monde qui parle d'extensions manque toujours les 2 faits importants sur Mysqli et AOP:

  1. L'instruction préparée n'est pas une solution miracle . Il existe des identificateurs dynamiques qui ne peuvent pas être liés à l'aide d'instructions préparées. Il existe des requêtes dynamiques avec un nombre inconnu de paramètres, ce qui rend la construction de requête difficile.

  2. Ni mysqli_ * ni les fonctions PDO n'auraient dû apparaître dans le code de l'application.
    Il devrait exister une couche d'abstraction entre eux et le code de l'application, ce qui ferait tout le sale boulot de liaison, de bouclage et de gestion des erreurs. , etc. à l'intérieur, en rendant le code d'application DRY et en le nettoyant. Surtout pour les cas complexes comme la construction de requêtes dynamiques.

Il ne suffit donc pas de passer à PDO ou à mysqli. Il faut utiliser un ORM, un générateur de requêtes ou une classe d'abstraction de base de données au lieu d'appeler des fonctions d'API brutes dans leur code.
Et contrairement - si vous avez une couche d’abstraction entre votre code d’application et votre API mysql - , le moteur utilisé importe peu Vous pouvez utiliser mysql ext jusqu’à ce qu’il devienne obsolète, puis réécrire facilement votre classe d’abstraction sur un autre moteur en conservant tout le code de l’application.

Voici quelques exemples basés sur my safemysql class pour montrer comment une telle classe d'abstraction devrait être:

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

Comparez cette seule ligne avec quantité de code dont vous aurez besoin avec PDO .
Puis comparez avec quantité de code folle vous aurez besoin des instructions brutes préparées par Mysqli. Notez que la gestion des erreurs, le profilage et la consignation des requêtes sont déjà intégrés et en cours d'exécution.

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

Comparez-le avec les insertions PDO habituelles, lorsque chaque nom de champ est répété six à dix fois - dans tous ces nombreux espaces réservés nommés, liaisons et définitions de requête.

Un autre exemple:

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

Il est difficile de trouver un exemple permettant à PDO de gérer un tel cas pratique.
Et ce sera trop verbeux et probablement dangereux.

Donc, encore une fois - il ne s’agit pas uniquement d’un pilote brut, mais d’une classe d’abstraction, utile non seulement pour les exemples ridicules tirés du manuel du débutant, mais également pour résoudre tous les problèmes de la vie réelle.

104
Your Common Sense

Il existe de nombreuses raisons, mais la plus importante est peut-être que ces fonctions encouragent les pratiques de programmation peu sûres car elles ne supportent pas les déclarations préparées. Les instructions préparées aident à prévenir les attaques par injection SQL.

Lorsque vous utilisez les fonctions mysql_*, vous devez vous rappeler d’exécuter les paramètres fournis par l’utilisateur via mysql_real_escape_string(). Si vous oubliez à un seul endroit ou s'il vous arrive d'échapper à une partie seulement de l'entrée, votre base de données peut être attaquée.

L'utilisation d'instructions préparées dans PDO ou mysqli fera en sorte que ces types d'erreurs de programmation soient plus difficiles à commettre.

88
Trott

Parce que (entre autres raisons), il est beaucoup plus difficile d’assurer la désinfection des données d’entrée. Si vous utilisez des requêtes paramétrées, comme avec PDO ou mysqli, vous pouvez entièrement éviter le risque.

Par exemple, quelqu'un pourrait utiliser "enhzflep); drop table users" comme nom d'utilisateur. Les anciennes fonctions permettent d’exécuter plusieurs instructions par requête, ce qui permet de supprimer tout un tableau comme celui-ci.

Si on utilisait PDO de mysqli, le nom d'utilisateur serait "enhzflep); drop table users".

Voir bobby-tables.com .

71
enhzflep

Cette réponse est écrite pour montrer à quel point il est trivial de contourner le code de validation utilisateur mal écrit PHP, comment (et en utilisant quoi) ces attaques fonctionnent et comment remplacer les anciennes fonctions MySQL par une instruction sécurisée préparée - et fondamentalement , pourquoi les utilisateurs de StackOverflow (qui ont probablement beaucoup de représentants) crieront aux nouveaux utilisateurs en leur posant des questions pour améliorer leur code.

Tout d’abord, n'hésitez pas à créer cette base de données mysql test (j’ai appelé le mien prep):

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)

Ceci fait, nous pouvons passer à notre code PHP.

Supposons que le script suivant est le processus de vérification d'un administrateur sur un site Web (simplifié mais fonctionnel si vous le copiez et l'utilisez à des fins de test):

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

Semble assez légitime à première vue.

L'utilisateur doit entrer un identifiant et un mot de passe, non?

Brillant, n'entrez pas dans ce qui suit:

user: bob
pass: somePass

et le soumettre.

La sortie est la suivante:

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

Super! Travaillant comme prévu, essayons maintenant le nom d’utilisateur et le mot de passe réels:

user: Fluffeh
pass: mypass

Incroyable! Salut-cinq partout, le code a correctement vérifié un administrateur. C'est parfait!

Eh bien pas vraiment. Disons que l'utilisateur est une petite personne intelligente. Disons que la personne est moi.

Entrez ce qui suit:

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

Et le résultat est:

The check passed. We have a verified admin!

Félicitations, vous venez de me permettre de saisir votre section réservée aux administrateurs super-protégés avec un faux nom d'utilisateur et un faux mot de passe. Sérieusement, si vous ne me croyez pas, créez la base de données avec le code que j'ai fourni et exécutez ce code PHP - qui, en un coup d'œil, semble réellement vérifier le nom d'utilisateur et le mot de passe plutôt bien.

Donc, en réponse, QUE IS POURQUOI VOUS ÊTES JOUÉ À.

Voyons donc ce qui ne va pas et pourquoi je viens de rentrer dans votre caverne super-admin-seulement-batte. J'ai pris une conjecture et supposé que vous n'étiez pas prudent avec vos entrées et les ai simplement passées directement à la base de données. J'ai construit l'entrée de manière à CHANGER la requête que vous exécutiez réellement. Alors, qu'est-ce que c'était supposé être et qu'est-ce que cela a fini par être?

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

C'est la requête, mais lorsque nous remplaçons les variables par les entrées réelles que nous avons utilisées, nous obtenons ce qui suit:

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

Voyez comment j'ai construit mon "mot de passe" de sorte qu'il ferme d'abord la citation simple autour du mot de passe, puis introduit une comparaison totalement nouvelle? Ensuite, juste pour des raisons de sécurité, j'ai ajouté une autre "chaîne" afin que le guillemet simple soit fermé comme prévu dans le code que nous avions à l'origine.

Cependant, il ne s'agit pas de gens qui vous crient dessus maintenant, mais de vous montrer comment rendre votre code plus sécurisé.

Ok, alors qu'est-ce qui a mal tourné et comment pouvons-nous résoudre le problème?

C'est une attaque d'injection SQL classique. L'un des plus simples à cet égard. Sur l’échelle des vecteurs d’attaque, c’est un enfant en bas âge qui attaque un char - et qui gagne.

Alors, comment pouvons-nous protéger votre section d'administration sacrée et la rendre agréable et sécurisée? La première chose à faire sera de cesser d'utiliser ces fonctions mysql_* vraiment anciennes et obsolètes. Je sais, vous avez suivi un tutoriel que vous avez trouvé en ligne et cela fonctionne, mais il est vieux, il est obsolète et en quelques minutes, je viens de le dépasser sans casser la sueur.

Maintenant, vous avez les meilleures options d’utiliser mysqli_ ou PDO . Personnellement, je suis un grand fan de PDO, je vais donc utiliser PDO dans la suite de cette réponse. Il y a des avantages et des inconvénients, mais personnellement, je trouve que les avantages l'emportent largement sur les inconvénients. Il est portable sur plusieurs moteurs de base de données - que vous utilisiez MySQL ou Oracle ou à peu près n'importe quoi - mais en changeant la chaîne de connexion, il possède toutes les fonctionnalités de fantaisie que nous voulons utiliser. Il est propre et agréable. J'aime nettoyer.

Maintenant, regardons à nouveau ce code, écrit cette fois en utilisant un objet 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>

Les principales différences sont qu’il n’ya plus de fonctions mysql_*. Tout se fait via un objet PDO, deuxièmement, il utilise une instruction préparée. Maintenant, quelle est une déclaration préparée vous demandez? C'est un moyen de dire à la base de données avant d'exécuter une requête, quelle est la requête que nous allons exécuter. Dans ce cas, nous disons à la base de données: "Bonjour, je vais exécuter une instruction select demandant id, userid et pass from la table users, où id est une variable et où pass est aussi une variable.".

Ensuite, dans l'instruction execute, nous transmettons à la base de données un tableau contenant toutes les variables attendues.

Les résultats sont fantastiques. Essayons à nouveau ces combinaisons de nom d'utilisateur et de mot de passe

user: bob
pass: somePass

L'utilisateur n'a pas été vérifié. Impressionnant.

Que diriez-vous:

user: Fluffeh
pass: mypass

Oh, je viens juste d’être un peu excité, cela a fonctionné: le chèque est passé. Nous avons un administrateur vérifié!

Essayons maintenant les données qu'un type malin entrerait pour essayer de contourner notre petit système de vérification:

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

Cette fois, nous obtenons ce qui suit:

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

C'est pourquoi on vous crie après avoir posé des questions - c'est parce que les gens peuvent voir que votre code peut être contourné sans même essayer. Utilisez cette question et ces réponses pour améliorer votre code, le rendre plus sécurisé et utiliser les fonctions actuelles.Enfin, cela ne veut pas dire qu'il s'agit d'un code PARFAIT. Vous pouvez faire beaucoup d’autres choses pour l’améliorer: utilisez des mots de passe hachés, par exemple, assurez-vous que lorsque vous stockez des informations sensibles dans la base de données, vous ne les stockez pas en texte brut, vous disposez de plusieurs niveaux de vérification - mais réellement, si vous changez simplement votre ancien code sujet à l’injection, vous serez BIEN en train d’écrire un bon code - et le fait que vous ayez atteint ce stade et que vous lisiez encore me donne l’espoir que vous ne mettrez pas en œuvre ce type uniquement. de code lors de la rédaction de vos sites Web et de vos applications, mais que vous puissiez aller faire des recherches sur les autres choses que je viens de mentionner - et plus encore. Écrivez le meilleur code possible, pas le code le plus élémentaire qui fonctionne à peine.

Lastly, this isn't to say that this is PERFECT code. There are many more things that you could do to improve it, use hashed passwords for example, ensure that when you store sensetive information in the database, you don't store it in plain text, have multiple levels of verification - but really, if you just change your old injection prone code to this, you will be WELL along the way to writing good code - and the fact that you have gotten this far and are still reading gives me a sense of hope that you will not only implement this type of code when writing your websites and applications, but that you might go out and research those other things I just mentioned - and more. Write the best code you can, not the most basic code that barely functions.

62
Fluffeh

L'extension MySQL est la plus ancienne des trois et était le moyen original par lequel les développeurs communiquaient avec MySQL. Cette extension est maintenant obsolète en faveur de l'autre deuxalternatives en raison d'améliorations apportées aux nouvelles versions de PHP et de MySQL.

  • MySQLi est l'extension "améliorée" permettant de travailler avec des bases de données MySQL. Il tire parti des fonctionnalités disponibles dans les versions plus récentes du serveur MySQL, expose au développeur une interface orientée fonction et une interface orientée objet, ainsi que quelques autres choses astucieuses.

  • PDO offre une API qui consolide la plupart des fonctionnalités qui étaient auparavant dispersées dans les principales extensions d'accès à la base de données, à savoir MySQL, PostgreSQL, SQLite, MSSQL, etc. avec les connexions de base de données, les requêtes et les jeux de résultats, et les pilotes de bas niveau assurent la communication et la gestion des ressources avec le serveur de base de données. La discussion et le travail vont bon train dans PDO et cela est considéré comme la méthode appropriée pour travailler avec des bases de données dans un code professionnel moderne.

30
Alexander

Je trouve les réponses ci-dessus très longues, donc pour résumer:

L'extension mysqli a un nombre de avantages, les améliorations clés par rapport à l'extension mysql étant:

  • Interface orientée objet
  • Prise en charge des déclarations préparées
  • Prise en charge de plusieurs déclarations
  • Prise en charge des transactions
  • Capacités de débogage améliorées
  • Prise en charge du serveur intégré

Source: Vue d'ensemble de MySQLi


Comme expliqué dans les réponses ci-dessus, les alternatives à mysql sont mysqli et PDO (PHP Data Objects).

  • L'API prend en charge les instructions préparées côté serveur: prises en charge par MYSQLi et PDO
  • API prend en charge les instructions préparées côté client: prises en charge uniquement par PDO
  • API prend en charge les procédures stockées: MySQLi et PDO
  • L'API prend en charge les instructions multiples et toutes les fonctionnalités de MySQL 4.1+ - Pris en charge par MySQLi et surtout aussi par PDO

MySQLi et PDO ont tous deux été introduits dans PHP 5.0, alors que MySQL était introduit avant PHP 3.0. Un point à noter est que MySQL est inclus dans PHP5.x bien que déconseillé dans les versions ultérieures. 

19
Ani Menon

Il est possible de définir presque toutes les fonctions mysql_* en utilisant mysqli ou PDO. Incluez-les simplement sur votre ancienne application PHP, et cela fonctionnera sous PHP7. Ma solution ici .

<?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;
}
2
Pavel Tzonkov

Les fonctions qui ressemblent autant à ce type mysql_connect(), mysql_query() sont les fonctions de la version précédente PHP i.e (PHP 4) et ne sont plus utilisées.

Ceux-ci sont remplacés par mysqli_connect(), mysqli_query() de la même manière dans la dernière version de PHP5.

C'est la raison derrière l'erreur.

0
Killer

C'est une vieille question aujourd'hui (janvier 2019), mais cela pourrait encore être utile. Il y a environ 7 ans, j'ai créé un mappage tabulaire de la fonctionnalité MySQL/MySQLi/PDO. Peut être une référence utile. C'est en ligne ici et reproduit ci-dessous. N'hésitez pas à copier et coller le code HTML.

Sur le plan pratique, j’ai constaté que la conversion des fonctions procédurales de MySQL en OOP, MySQLi, était la voie de la moindre résistance. C'est parfaitement bien d'avoir deux connexions de base de données ouvertes en même temps, ce qui nous a donné une certaine flexibilité lors de la conversion - nous avons pu convertir les scripts de manière fragmentée, requête par requête. Bien que je ne le recommande peut-être pas aujourd'hui, c'était opportun à l'époque.

<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 -->
0
Ray Paseur