PHP propose trois API différentes pour se connecter à MySQL. Ce sont les mysql
(supprimés à partir de PHP 7) mysqli
, et les PDO
extensions.
Les mysql_*
fonctions étaient très populaires, mais leur utilisation n'est plus encouragée. L'équipe de documentation discute de la situation de la sécurité de la base de données et éduquer les utilisateurs à s'éloigner de l'extension ext / mysql couramment utilisée en fait partie (consultez php.internals: déprécier ext / mysql ).
Et l'équipe de développement de PHP plus tard a pris la décision de générer des E_DEPRECATED
erreurs lorsque les utilisateurs se connectent à MySQL, que ce soit par mysql_connect()
, mysql_pconnect()
ou la fonctionnalité de connexion implicite intégrée dans ext/mysql
.
ext/mysql
a été officiellement déconseillé depuis PHP 5.5 et a été supprimé depuis PHP 7 .
Voir la boîte rouge?
Lorsque vous allez sur n'importe quelle mysql_*
page de manuel de fonction, vous voyez une boîte rouge, expliquant qu'elle ne devrait plus être utilisée.
Pourquoi
S'éloigner ext/mysql
n'est pas seulement une question de sécurité, mais aussi d'avoir 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, tout en conservant la compatibilité avec cette ancienne version, ce qui rend le code un peu plus difficile à maintenir. Les fonctionnalités manquantes qui ne sont pas prises en charge ext/mysql
incluent: (à partir du manuel PHP ).
Raison de ne pas utiliser la mysql_*
fonction :
- Pas en développement actif
- Supprimé à partir de PHP 7
- Manque une interface OO
- Ne prend pas en charge les requêtes asynchrones non bloquantes
- Ne prend pas en charge les instructions préparées ou les requêtes paramétrées
- Ne prend pas en charge les procédures stockées
- Ne prend pas en charge plusieurs instructions
- Ne prend pas en charge les transactions
- Ne prend pas en charge toutes les fonctionnalités de MySQL 5.1
Ci-dessus point 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 les échapper manuellement avec un appel de fonction distinct.
Voir la comparaison des extensions SQL .
Suppression des avertissements de dépréciation
Pendant la conversion du code en MySQLi
/ PDO
, les E_DEPRECATED
erreurs peuvent être supprimées en définissant error_reporting
dans php.ini pour exclureE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Notez que cela masquera également d' autres avertissements de dépréciation , qui peuvent cependant concerner d'autres choses que MySQL. ( du manuel PHP )
L'article PDO vs MySQLi: lequel utiliser? par Dejan Marjanovic vous aidera à choisir.
Et une meilleure façon est PDO
, et j'écris maintenant un PDO
tutoriel simple .
Un tutoriel PDO simple et court
Q. Ma première question était: qu'est-ce que «AOP»?
A. « PDO - PHP Data Objects - est une couche d'accès à la base de données fournissant une méthode uniforme d'accès à plusieurs bases de données.»
Connexion à MySQL
Avec mysql_*
fonction 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
: il vous suffit de créer un nouvel PDO
objet. Le constructeur accepte les paramètres pour spécifier la source de base de données PDO
constructeur de la plupart prend quatre paramètres qui sont DSN
(nom de la source de données) et le cas échéant username
, password
.
Ici, je pense que vous connaissez tout sauf DSN
; c'est nouveau dans PDO
. A DSN
est essentiellement une chaîne d'options indiquant le PDO
pilote à utiliser et les détails de connexion. Pour plus d'informations, 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 parfois cela provoque une erreur, il est donc préférable de l'utiliser utf8
.
S'il y a une erreur de connexion, il lancera un PDOException
objet qui peut être capturé pour être manipulé Exception
davantage.
Bonne lecture : Connexions et gestion des connexions ¶
Vous pouvez également passer 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 PDO
pilotes ne prennent pas en charge les instructions préparées natives, PDO
exécute donc l' émulation de la préparation. Il vous permet également d'activer manuellement cette émulation. Pour utiliser les instructions natives préparées côté serveur, vous devez les définir explicitement false
.
L'autre consiste à désactiver la préparation de l'émulation qui est activée dans le MySQL
pilote par défaut, mais la préparation de l'émulation doit être désactivée pour une utilisation en PDO
toute sécurité.
J'expliquerai plus tard pourquoi la préparation de l'émulation doit être désactivée. Pour trouver la raison, veuillez consulter cet article .
Il n'est utilisable que si vous utilisez une ancienne version MySQL
dont 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 de PDO?
Oui , nous pouvons également définir certains attributs après la construction de PDO avec la setAttribute
méthode:
$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 simple PDO
que mysql_*
.
Une pratique courante lors de l'utilisation mysql_*
est:
//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 die
. Il terminera simplement le script brusquement, puis fera écho de l'erreur à l'écran que vous ne voulez généralement pas montrer à vos utilisateurs finaux, et permettra aux pirates sanglants de découvrir votre schéma. Alternativement, les valeurs de retour des mysql_*
fonctions peuvent souvent être utilisées en conjonction avec mysql_error () pour gérer les erreurs.
PDO
offre une meilleure solution: les exceptions. Tout ce que nous faisons avec PDO
devrait être enveloppé dans un try
- catch
bloc. Nous pouvons forcer PDO
dans l'un des trois modes d'erreur en définissant l'attribut de mode d'erreur. Trois modes de gestion des erreurs sont présentés ci-dessous.
PDO::ERRMODE_SILENT
. Il s'agit simplement de définir des codes d'erreur et agit à peu près de la même manière que mysql_*
lorsque vous devez vérifier chaque résultat, puis regarder $db->errorInfo();
pour obtenir les détails de l'erreur.
PDO::ERRMODE_WARNING
Relevez E_WARNING
. (Avertissements d'exécution (erreurs non fatales). L'exécution du script n'est pas interrompue.)
PDO::ERRMODE_EXCEPTION
: Lève des exceptions. Il représente une erreur déclenchée par PDO. Vous ne devez pas jeter un PDOException
de votre propre code. Voir Exceptions pour plus d'informations sur les exceptions en PHP. Il agit très bien comme or die(mysql_error());
, quand il n'est pas attrapé. Mais contrairement à or die()
, le PDOException
peut être attrapé et manipulé 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 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
- en catch
ce moment. Vous pouvez l'attraper à tout moment approprié, mais je vous recommande fortement d'utiliser try
- catch
. Il peut également être plus judicieux de l'attraper en dehors de la fonction qui appelle le PDO
truc:
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.
}
De plus, 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 tournant display_errors off
et en lisant simplement votre journal des erreurs.
Maintenant, après avoir lu toutes les choses ci - dessus, vous pensez probablement: ce que le diable est que quand je veux juste commencer à se penchant simples SELECT
, INSERT
, UPDATE
, ou DELETE
déclarations? Ne vous inquiétez pas, c'est parti:
Sélection des données
Donc, ce que vous faites, mysql_*
c'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 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 PDOStatement
objet. 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 ->fetch()
, une méthode de votre descripteur d'instructions. Avant d'appeler la récupération, la meilleure approche serait de dire à PDO comment vous souhaitez que les données soient récupérées. Dans la section ci-dessous, j'explique cela.
Modes de récupération
Notez l'utilisation de PDO::FETCH_ASSOC
dans le code fetch()
et fetchAll()
ci-dessus. Cela indique PDO
de renvoyer les lignes sous forme de tableau associatif avec les noms de champ comme clés. Il existe également de nombreux autres modes de récupération que j'expliquerai un par 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:
Maintenant j'arrive en mode fetch:
PDO::FETCH_ASSOC
: retourne un tableau indexé par nom de colonne tel que 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 le numéro de colonne indexé par 0, tel que renvoyé dans votre jeu de résultats
Il y a encore plus de choix! Lisez-les tous dans la PDOStatement
documentation Fetch. .
Obtenir le nombre de lignes :
Au lieu d'utiliser mysql_num_rows
pour obtenir le nombre de lignes retournées, vous pouvez obtenir un PDOStatement
et faire rowCount()
, comme:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Obtention du 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 instructions
Ce que nous faisons en mysql_*
fonction, c'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écutez une instruction SQL et retourne le nombre de lignes affectées.
L'insertion et la suppression seront traitées ultérieurement.
La méthode ci-dessus n'est utile que lorsque vous n'utilisez pas de variable dans la requête. Mais lorsque vous devez utiliser une variable dans une requête, n'essayez jamais comme ci-dessus et là pour l' instruction préparée ou l'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 ( cité dans Wikipedia trois points 3 ):
Préparer : 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 ne sont pas 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 (?, ?)
Le SGBD analyse, compile et effectue l'optimisation des requêtes sur le modèle d'instruction et stocke le résultat sans l'exécuter.
- Exécuter : ultérieurement, 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 SQL. Il y en a essentiellement trois sans espaces réservés (n'essayez pas avec la variable ci-dessus), un avec des espaces réservés sans nom et un avec des espaces réservés nommés.
Q. Alors maintenant, quels sont les espaces réservés nommés et comment 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 / de l'ordre de valeur dans le nom de la marque de réservation:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Vous pouvez également lier à l'aide d'un tableau d'exécution:
<?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 OOP
amis est que les espaces réservés nommés ont la possibilité d'insérer des objets directement dans votre base de données, en supposant 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 sans nom et comment 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 les voir ?
au lieu d'un nom comme dans un espace réservé au nom. Maintenant, dans le premier exemple, nous affectons des variables aux différents espaces réservés ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Ensuite, nous attribuons 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 le second au second ?
.
REMARQUE : dans les espaces réservés sans nom, nous devons prendre soin du bon ordre des éléments du tableau que nous transmettons à la PDOStatement::execute()
méthode.
SELECT
, INSERT
, UPDATE
, DELETE
Préparé les requêtes
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);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
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érifiez la réponse Les instructions préparées par PDO sont-elles suffisantes pour empêcher l'injection SQL? par 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 /*"));