utilisées - tableau php mysql




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

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


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 à ne plus utiliser les fonctions obsolètes de mysql_.

Les API de bases de données contemporaines sont simplement plus faciles à utiliser.

Ce sont principalement les paramètres liés qui peuvent simplifier le code. Et avec d' share la transition vers le 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 passer des anciennes fonctions mysql_ avec un minimum d’effort . Il ajoute des wrappers de fonctions pdo_ qui remplacent leurs équivalents mysql_ .

  1. Il suffit d' include_once( <pdo_mysql.php> ); dans chaque script d'invocation devant interagir avec la base de données.

  2. Supprimez le préfixe mysql_ partout et remplacez-le par pdo_ .

    • mysql_ connect() devient pdo_ connect()
    • mysql_ query() devient pdo_ query()
    • mysql_ num_rows() devient num_rows()
    • mysql_ insert_id() devient pdo_ insert_id()
    • mysql_ fetch_array() devient fetch_array()
    • mysql_ fetch_assoc() devient fetch_assoc()
    • mysql_ real_escape_string() devient pdo_ real_escape_string()
    • etc...

  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 utilise PDO.
Il est maintenant temps de l' utiliser .

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 pdo_query() par des pdo_query() à pdo_query() .
  • Placez des points d'interrogation ? comme des espaces réservés où les variables étaient avant.
  • Supprimez ' guillemets simples qui contenaient 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 ? des espaces réservés appliqués, vous n'avez pas à vous soucier de cela:

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_ * autorise toujours l' un ou l'autre .
Juste n'échappez pas une variable et ne la liez pas dans la même requête.

  • La fonction d'espace réservé est fournie par le véritable PDO derrière elle.
  • Ainsi également permis :named listes d'espaces réservés :named ultérieurement.

Plus important encore, vous pouvez passer les variables $ _REQUEST [] en toute sécurité derrière toute requête. Lorsque les champs <form> soumis correspondent à la structure de la base de données, elle est encore plus courte:

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

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

Corrige ou supprime toute fonction oldschool sanitize()

Une fois que vous avez converti tous les appels mysql_ en pdo_query avec des paramètres liés, supprimez tous les appels redondants pdo_real_escape_string .

En particulier, vous devez corriger toute fonction clean_data or clean_data ou clean_data ou clean_data comme annoncé 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.

  • Le bon ordre aurait été: stripslashes comme appel le plus interne, puis trim , puis strip_tags , htmlentities pour le contexte de sortie, et enfin la _escape_string tant qu’application devrait précéder directement l’intercalaire SQL.

  • Mais dans un premier temps, supprimez _real_escape_string 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. Ajouter un commentaire qu'il applique uniquement HTML é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 de désinfection, cela peut indiquer un niveau de surveillance supérieur.

    Note historique sur magic_quotes. Cette fonctionnalité est à juste titre déconseillée. Cependant, il est souvent décrit à tort comme une fonctionnalité de sécurité défaillante. 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 simplement "les guillemets seront automatiquement échappés, ce qui facilitera la transmission des données de formulaire directement aux requêtes msql ". Notamment, son utilisation avec mSQL était accidentellement sûre, car elle ne supportait que l'ASCII.
    Puis PHP3 / Zend a réintroduit magic_quotes pour MySQL et l'a mal documenté. Mais à l’origine, c’était une simple commodité , 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 se produisent simplement lorsque les données sont insérées dans le contexte du code . Un serveur de base de données ne peut pas localiser par la suite les variables PHP collées à l'origine 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 devriez vous soucier des avantages de lisibilité de la suppression de 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) grâce à cette séparation visible et technique des données / codes.

Attention, la liaison de paramètres n'est toujours pas une solution unique et magique contre toutes les 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 pdo_* wrapper 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

Nous avons donc, espérons-le, visualisé quelques raisons pratiques et une voie utile pour abandonner mysql_ .

Passer à pdo ne pdo pas. pdo_query() est aussi juste une interface dessus.

Sauf si vous introduisez également la liaison de paramètres 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.Il suffit de google pour l' abstraction de base de données PHP et parcourir 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 comme Paris/Idiorm . Tout comme personne n'utilise plus le DOM fade en JavaScript, vous n'avez plus besoin de garder une interface de base de données brute.


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

share . Ils ne sont plus maintenus deprecated . Voir la boîte rouge ? En savoir plus sur les instructions préparées et utiliser PDO ou MySQLi - cet article vous aidera à choisir laquelle. 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 deprecated

    Plus récent! ext / mysql a été supprimé de PHP 7 .

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

    mysql_* extension mysql_* ne prend pas en charge les instructions préparées , ce qui constitue (entre autres) une contre-mesure très efficace contre l' injection SQL . 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êcher l'injection SQL en PHP?

  • Voir la boîte rouge?

    Quand vous allez sur une page de manuel de fonction mysql , vous voyez une boîte rouge, expliquant qu’elle ne devrait plus être utilisée.

  • Utilisez soit PDO ou MySQLi

    PDO , qui offre une approche complète de la POO pour l’interaction avec la base de données, et MySQLi , qui est une amélioration spécifique de MySQL.


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

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 (consultez php.internals: annulation de ext / mysql ).

Et la dernière équipe de développeurs PHP a pris la décision de générer des erreurs E_DEPRECATED 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 deprecated et a été supprimé à partir de PHP 7 .

Voir la boîte rouge?

Quand vous allez sur une page de manuel de fonction mysql_* , vous voyez une boîte rouge, expliquant qu’elle ne devrait plus être utilisée.

Pourquoi

Quitter 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 é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, ce qui rend le code un peu plus difficile à maintenir. Les fonctionnalités manquantes qui ne sont pas supportées par ext/mysql incluent: ( deprecated ).

Raison de ne pas utiliser la fonction mysql_* :

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

Ci-dessus cité de 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 la conversion du code en MySQLi / PDO , les erreurs E_DEPRECATED 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 concerner des éléments autres que MySQL. ( du manuel PHP )

L'article PDO contre MySQLi: Lequel devriez-vous utiliser? par Dejan Marjanovic vous aidera à choisir.

Et un meilleur moyen est PDO , et je suis en train d’écrire un simple tutoriel sur les PDO .

Un tutoriel PDO simple et bref

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

R. « PDO - PHP Data Objects - est une couche d’accès à une base de données fournissant une méthode uniforme d’accès à plusieurs bases de données.»

Connexion à MySQL

Avec la fonction mysql_* ou on peut le dire à l'ancienne (obsolète en 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. Le constructeur de PDO principalement quatre paramètres, à savoir DSN (nom de source de données) et éventuellement username , password .

Ici, je pense que vous connaissez tout sauf le DSN ; c'est nouveau dans le PDO . Un DSN est essentiellement une chaîne d'options qui indique à PDO pilote à utiliser et les détails de la connexion. Pour plus de détails, 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 provoque parfois une erreur, il est donc préférable d'utiliser utf8 .

En cas d'erreur de connexion, un objet PDOException peut être intercepté pour gérer davantage l' 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 effectue donc une émulation de la préparation. Il vous permet également d'activer manuellement cette émulation. Pour utiliser les instructions préparées côté serveur natives, vous devez explicitement le définir sur 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 toute sécurité.

J'expliquerai plus tard pourquoi préparer l'émulation devrait être désactivée. Pour trouver la raison s'il vous plaît vérifier ce post .

Il n'est utilisable que si vous utilisez une ancienne version de MySQL ce 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));

Pouvons-nous 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

La gestion 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 devrait ê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 regarder $db->errorInfo(); pour obtenir les détails de l'erreur.
  • PDO::ERRMODE_WARNING Raise E_WARNING . (Avertissements d'exécution (erreurs non fatales). L'exécution du script n'est pas arrêtée.)
  • PDO::ERRMODE_EXCEPTION : PDO::ERRMODE_EXCEPTION exceptions. Cela représente une erreur soulevée par PDO. Vous ne devriez pas lancer une 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 attrapé. Mais contrairement à or die() , l’ PDOException peut être interceptée et gérée correctement si vous le souhaitez.

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'avez pas à gérer avec try - catch ce moment. Vous pouvez le saisir à tout moment, mais je vous recommande fortement d'utiliser try - catch . En outre, il peut être plus judicieux de le capturer en dehors de la fonction qui appelle la fonction 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 gérer 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 désactivant display_errors off et en lisant simplement votre journal des erreurs.

Maintenant, après avoir lu toutes les choses ci-dessus, vous vous demandez probablement: qu’est-ce que c’est quand je veux commencer à appuyer des SELECT simples SELECT , INSERT , UPDATE ou DELETE ? Ne t'inquiète pas, c'est parti:

Sélection des données

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 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 ->fetch() , une méthode de votre descripteur d’instruction. Avant d'appeler chercher, la meilleure approche serait d'indiquer à PDO comment vous souhaitez que les données soient récupérées. J'explique ceci dans la section ci-dessous.

Modes de récupération

Notez l'utilisation de PDO::FETCH_ASSOC dans les PDO::FETCH_ASSOC fetch() et fetchAll() ci-dessus. Cela 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 de récupération 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 : retourne un tableau indexé par nom de colonne tel qu'il est retourné dans votre jeu de résultats
  • PDO::FETCH_BOTH (valeur par défaut): retourne 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 la documentation PDOStatement Fetch. .

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 rowCount() :

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

Obtenir le dernier ID 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

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écute 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 comme ce qui est décrit ci-dessus, car il existe une instruction préparée ou une instruction paramétrée .

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 ( tiré de Wikipedia trois 3 points ):

  1. Préparer : le modèle de relevé 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 (en 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 pourrait fournir «Pain» pour le premier paramètre et 1.00 pour le second 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. 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 de la programmation 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. 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 ceux-ci ? au lieu d'un nom comme dans un nom titulaire. 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 passe au premier ? et la seconde à la seconde ? .

REMARQUE : Dans les espaces réservés non nommés, nous devons prendre en compte le bon ordre des éléments du tableau que nous transmettons à la 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();
    

REMARQUE:

Cependant, PDO et / ou MySQLi ne sont pas complètement sûrs. Vérifier la réponse Les instructions préparées par PDO sont-elles suffisantes pour empêcher l'injection SQL? par ircmaxell . En outre, 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 /*"));

Les fonctions mysql_ * sont obsolètes (à partir de PHP 5.5 ) car de meilleures fonctions et structures de code ont été développées. Le fait que la fonction ait été obsolète signifie qu’aucun effort supplémentaire ne sera consacré à l’améliorer en termes de performances et de sécurité, ce qui signifie qu’elle est moins à l’avenir .

Si vous avez besoin de plus de raisons:

  • Les fonctions mysql_ * ne supportent pas les instructions préparées.
  • Les fonctions mysql_ * ne supportent pas la liaison de paramètres.
  • Les fonctions mysql_ * manquent de fonctionnalités pour la programmation orientée objet.
  • la liste continue ...

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

L'extension mysqli présente de nombreux avantages, les principales améliorations 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 antérieur à PHP 3.0. Un point à noter est que MySQL est inclus dans PHP5.x bien que déconseillé dans les versions ultérieures.


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

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

L'extension MySQL est la plus ancienne des trois et était le moyen original par lequel les développeurs communiquent avec MySQL. Cette extension est maintenant deprecated au profit des mysqli_ autres PDO raison des 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 réparties entre les principales extensions d’accès à la base de données, à savoir MySQL, PostgreSQL, SQLite, MSSQL, etc. L’interface expose des objets de haut niveau permettant au programmeur de travailler avec les connexions, les les ensembles de résultats et les pilotes de bas niveau établissent la communication et la gestion des ressources avec le serveur de base de données. PDO suscite beaucoup de discussions et de travail et est considéré comme la méthode appropriée pour travailler avec des bases de données dans un code professionnel moderne.


Les mysql_fonctions:

  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

Parce que (entre autres raisons), il est beaucoup plus difficile d’assainir les 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 une table entière.

Si l’on utilisait PDO de mysqli, le nom de l’utilisateur finirait par être "enhzflep); drop table users".

Voir bobby-tables.com .


Parlant de raisons techniques , il n’en existe 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, par exemple

  • 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 à une solution plus élégante et moderne.

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

  • L'utilisation ultérieure de ces fonctions avec les versions modernes de 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 version par défaut de PHP. Ce n'est pas un problème, 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é . Vous mentionnez souvent ces fonctions honnêtes, on vous dit qu'elles sont strictement taboues.
  • En tant qu'utilisateur moyen de PHP, votre idée d'utiliser ces fonctions est 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 que tous ces utilisateurs de PHP apprennent à gérer correctement les requêtes SQL en même temps. Très probablement, ils changeraient simplement mysql_ * en mysqli_ * mécaniquement, laissant l'approche identique . Surtout parce que mysqli rend l'utilisation des déclarations préparées incroyablement douloureuse et gênante.
Sans oublier que les instructions préparées natives ne suffisent pas à protéger contre les injections SQL, 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.

En outre, il existe des raisons fausses ou non significatives, telles que

  • Ne supporte pas les procédures stockées (que nous utilisions mysql_query("CALL my_proc");depuis longtemps)
  • 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 (alors quoi? Cela vous affecte-t-il de manière pratique?)
  • Manque une interface OO (en créer une 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 natives , elles ne sont pas nécessaires à 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);

le tour est joué , 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 une 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 prend en charge de nombreuses fonctionnalités spécifiques à mysql.
  • Si, comme la grande majorité des utilisateurs de PHP, 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, avec deux d'entre elles distingue PDO 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 de 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 et a ses difficultés.
J'ai donc écrit des solutions à tous les pièges courants et aux cas complexes dans le wiki des balises PDO.

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

  1. Une déclaration 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 y avoir une couche d’abstraction entre eux et le code de l’application, ce qui fera tout le travail salissant de la liaison, de la mise en boucle, de la gestion des erreurs, etc. Surtout pour les cas complexes comme la construction de requêtes dynamiques.

Donc, il ne suffit 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 au contraire, si vous avez une couche d'abstraction entre votre code d'application et l'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 intact le code de l'application.

Voici quelques exemples basés sur ma classe safemysql 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 la quantité de code dont vous aurez besoin avec PDO .
Comparez ensuite avec une quantité folle de code dont vous aurez besoin avec les 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 du manuel du débutant, mais également pour la résolution de problèmes concrets.





database