Jérôme CUTRONA jerome.cutrona@univ-reims.fr PHP PDO Jérôme CUTRONA jerome.cutrona@univ-reims.fr 23:38:06 Programmation Web 2017-2018.

Slides:



Advertisements
Présentations similaires
MySQL et PHP.
Advertisements

PHP mySQL Extension php_mysql. Connexion à une base de données Établir une connexion mysql_connect(string server, string username, string password) –permet.
Jérôme CUTRONA PHP et bases de données Jérôme CUTRONA 01:07:51 Programmation Web
Jérôme CUTRONA PHP PDO Jérôme CUTRONA 01:08:01 Programmation Web
Développement d’applications web
Le langage PHP 5.
JDBC ou comment manipuler une base de données en Java ?
A4.1.1 Proposition dune solution applicative A4.1.2 Conception ou adaptation de linterface utilisateur dune solution applicative A4.1.2 Conception ou.
1 PHP 1.Langage PHP 1.1. Types de base, variables et constantes 1.2. Opérateurs et expressions 1.3. Instructions 1.4. Fonctions 2.Accès aux bases de données:
MySQL Création des sites dynamiques
Christine Bonnet SOURCES : « Samples » dOracle, « Oracle 8 » R. Chapuis PRO*C – C ++
CPI/BTS 2 Programmation Web PHP et les Bases de données Prog Web CPI/BTS2 – M. Dravet – 11/03/2004 Dernière modification: 11/03/2004.
JDBC L'API JDBC est utilisée pour utilisée pour intéragir avec une base de données.
Jérôme CUTRONA PHP PDO Jérôme CUTRONA 07:21:24 Programmation Web
 Formulaires HTML : traiter les entrées utilisateur
Séance /10/2004 SGBD - Approches & Principes.
Bases de données Singleton pour la connexion
Logiciel Assistant Gestion d’Événement Rémi Papillié (Chef d’équipe) Maxime Brodeur Xavier Pajani Gabriel Rolland David St-Jean.
Système de gestion d'entrées pour un cinéma Scanvion Michel – Etudiant 4.
Toulibre : présentation de... par Rémy Sanchez +.
Développement à l'Aide de Langages Dynamiques : Smalltalk module IT308: Méthodologies et outils logiciels Nada Ayad, Damien Cassou et Annabelle Souc.
Oubliez SQL avec SQLAlchemy Pycon.fr 2009 Michael Scherer.
1- Introduction 1ère partie Le langage SQL 2- Connexion 3- Structure & Contenu 4- Requêtes.
SQL partie 5 1 LMD create – update – primary key secondary key.
SQL query - 1 / D. Berrabah SQL : interrogation de BD Requêtes d'interrogation simples Requêtes complexes Agrégats et groupement.
SQL query - 1 UPMC - UFR 919 Ingéniérie - Cours Bases de données (BD-L3) SQL : interrogation de BD Requêtes d'interrogation simples Requêtes complexes.
1 Programmation en C++ C++ de base ● Programme C++ ● Variables, objets, types ● Fonctions ● Namespace ● Tests ● Boucles ● Pointeurs, références.
1 Programmation en C++ C++ de base ● Programme C++ ● Variables, objets, types ● Types et opérations fondamentales ● Tests ● Boucles ● Pointeurs, références.
Cross-Plateform Cours JavaScript
Qui fait de la programmation Quels sont les besoins
Comment corriger son code sans forcément appeler l’enseignant M2202
Environnement de développement des BD
Introduction au Langage Pascal
Systèmes d’Information
Environnement du développement de BD ORACLE REPORTS 10g
I- ANALYSE DU BESOIN / Plan
Robotisation de la ligne transitique
ملخص Initiation à la sgbdr
PHP & MYSQL.
Intégration du P7 dans l’épreuve E41
Pas de variable globale
Les notions de classe et d'objet
5.
Détection des erreurs.
Langage de manipulation de données (LMD)
Principes de programmation (suite)
Les bases de données et le modèle relationnel
fonctionnalités iiS iis
Comment fonctionne RADIUS?
Asynchronous Javascript And Xml
5.
Réalisation d’une application web sous le thème: «Mon vétérinaire » par : Benzineb Asmaa et Meftahi Oualid Présentation à Université Saad Dahlab Blida.
Introduction BD TABLES ET DONNÉES champs OU données, types de données
Programmation en C++ C++ de base
Programmation Orientée Objet C# El Akel Bouchra ISMONTIC Tanger 2013/2014.
Programmation Android Bases De Données, SQL-lite
Calcul Scientifique Initiation à SCILB
1 Copyright © 2004, Oracle. Tous droits réservés. Extraire des données à l'aide de l'instruction SQL SELECT.
Bases de données sous Access. Initiation aux bases de données  Structure d’une base de données.
5 Interface entre SQL et un programme
Programmation PHP / PDO
Programmation Web : DOM en PHP
© Robert Godin. Tous droits réservés.
Lecture/Écriture de fichiers (I/O)
Variables et accès en Java
Jérôme CUTRONA PHP objet Jérôme CUTRONA 08:30:34 Programmation Web
© Robert Godin. Tous droits réservés.
Bases de données Singleton pour la connexion
© Robert Godin. Tous droits réservés.
Transcription de la présentation:

Jérôme CUTRONA jerome.cutrona@univ-reims.fr PHP PDO Jérôme CUTRONA jerome.cutrona@univ-reims.fr 23:38:06 Programmation Web 2017-2018

Introduction 23:38:06 Programmation Web 2017-2018

PDO PDO : PHP Data Objects Extension PHP fournissant une interface pour accéder à une base de données Fournit une interface d'abstraction pour l'accès aux données Ne fournit PAS une abstraction de base de données SQL spécifique au moteur Fonctionnalités présentes / absentes Interface orientée objet 23:38:06 Programmation Web 2017-2018

Bases de données supportées Nom du pilote Bases de données prises en charge PDO_CUBRID Cubrid PDO_DBLIB FreeTDS / Microsoft SQL Server / Sybase PDO_FIREBIRD Firebird PDO_IBM IBM DB2 PDO_INFORMIX IBM Informix Dynamic Server PDO_MYSQL MySQL 3.x/4.x/5.x PDO_OCI Oracle Call Interface PDO_ODBC ODBC v3 (IBM DB2, unixODBC et win32 ODBC) PDO_PGSQL PostgreSQL PDO_SQLITE SQLite 3 et SQLite 2 PDO_SQLSRV Microsoft SQL Server / SQL Azure PDO_4D 4D 23:38:06 Programmation Web 2017-2018

Échanges de données 23:38:06 Programmation Web 2017-2018

Échanges de données 1 2 6 7 3 4 8 5 9 Serveur Web Requête HTTP Script PHP 2 Exploitation résultats BD 6 Réponse HTTP 7 Connexion BD 3 Requête BD 4 Fin de script 8 Résultats BD 5 BD Affichage résultat 9 Mon PC Serveur BD 23:38:06 Programmation Web 2017-2018 6

Les classes de PDO 23:38:06 Programmation Web 2017-2018

Classes prédéfinies PDO : connexion PHP / base de données __construct() exec(), prepare(), query() errorCode(), errorInfo() getAttributes(), setAttribute() lastInsertId(), quote() beginTransaction() commit(), rollBack() getAvailableDrivers() 23:38:06 Programmation Web 2017-2018

Classes prédéfinies PDOStatement : requête préparée, jeu de résultats bindColumn(), bindParam(), bindValue(), closeCursor() errorCode(), errorInfo() fetch(), fetchAll(), fetchColumn(), fetchObject(), setFetchMode(), nextRowset() rowCount(), columnCount(), getColumnMeta() getAttribute(), setAttribute() execute() debugDumpParams() 23:38:06 Programmation Web 2017-2018

Comment se connecter ? 23:38:06 Programmation Web 2017-2018

Connexions et gestionnaire de connexion Instanciation d'un objet PDO $dbh=new PDO(DSN [, user [, pass [, options]]]); DSN : Data Source Name nom_du_driver:syntaxe_spécifique_au_driver Ex : 'mysql:host=localhost;dbname=ma_base' user : nom d'utilisateur, pass : mot de passe options : tableau associatif spécifiques au driver Ex : array(PDO::ATTR_PERSISTENT => true)); Fin de connexion : $dbh=null; /* ou */ unset($dbh); 23:38:06 Programmation Web 2017-2018

Gestion des erreurs 23:38:06 Programmation Web 2017-2018

Gestion des erreurs de connexion Connexion par construction d'un objet Gestion envisageable des erreurs Aucune Fin brutale (exit, die) État Exception En cas d'erreur de connexion Objet PDOException lancé PDOException hérite de Exception 23:38:06 Programmation Web 2017-2018

Gestion des erreurs de connexion <?php try { $dbh = new PDO('mysql:host=dbs;dbname=music', $user, $pass) ; … $dbh = null ; } catch (PDOException $e) { echo "<p>Erreur: ".$e->getMessage() ; die() ; 23:38:06 Programmation Web 2017-2018

Gestion des erreurs (hormis connexion) PDO::ERRMODE_SILENT (par défaut) Mode silencieux, mise en place d'un code d'erreur PDO : errorCode() / errorInfo() PDOStatement : errorCode() / errorInfo() PDO::ERRMODE_WARNING Mise en place du code d'erreur Émission d'une erreur de type E_WARNING PDO::ERRMODE_EXCEPTION Objet PDOException lancé 23:38:06 Programmation Web 2017-2018

Gestion des erreurs (hormis connexion) <?php try { $dbh = new PDO('mysql:host=dbs;dbname=music', $user, $pass) ; $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); … $dbh = null ; } catch (PDOException $e) { echo "<p>Erreur: ".$e->getMessage() ; die() ; 23:38:06 Programmation Web 2017-2018

Gestion des erreurs : code d'erreur <?php $pdo = new PDO("mysql:host=dbs;dbname=music") ; $pdostat = $pdo->query("COUCOU") ; if ($pdo->errorCode()) { echo "ERREUR !!\n" ; echo "<pre>\n" ; var_dump($pdo->errorInfo()) ; echo "</pre>\n" ; } Code SQLSTATE Code erreur spécifique du driver ERREUR !! array(3) { [0]=> string(5) "42000" [1]=> int(1064) [2]=> string(47) "Erreur de syntaxe près de 'COUCOU' à la ligne 1" } Chaîne erreur spécifique au driver 23:38:06 Programmation Web 2017-2018

Gestion des erreurs : exceptions <?php try { $pdo = new PDO("mysql:host=dbs;dbname=music") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) ; $pdostat = $pdo->query("COUCOU") ; } catch (Exception $e) { echo "<p>ERREUR : ".$e->getMessage() ; Code erreur spécifique du driver Chaîne erreur spécifique au driver Code SQLSTATE ERREUR : SQLSTATE[42000]: Syntax error or access violation: 1064 Erreur de syntaxe près de 'COUCOU' à la ligne 1 23:38:06 Programmation Web 2017-2018

Effectuer une requête 23:38:06 Programmation Web 2017-2018

Exécution d'une requête PDO::query ( string statement ): PDOStatement <?php try { $pdo = new PDO("mysql:host=dbs;dbname=music") ; $pdostat = $pdo->query("SELECT * FROM artist") ; } catch (Exception $e) { echo "<p>ERREUR : ".$e->getMessage() ; Requête Résultat de requête Une requête doit toujours être préparée ! Il est conservé le temps de quelques transparents query sera donc à bannir 23:38:06 Programmation Web 2017-2018

Exploitation des résultats d'une requête Récupération des données ligne à ligne Une ligne peut être : un tableau indexé un tableau associatif un tableau mixte (par défaut) un objet anonyme un objet d'une classe définie par l'utilisateur Récupération des données d'une colonne 23:38:06 Programmation Web 2017-2018

Parcours du résultat d'une requête Parcourir le résultat de la requête Résultat de requête SELECT * FROM morceau ORDER BY mor_id +--------+------------------------------------+ | mor_id | mor_nom | | 872 | With A Little Help From My Friends | | 873 | The Letter | | 874 | Marjorine | | 875 | Midnight Rider | | 876 | You Are So Beautiful | | 877 | Feelin' Allright | | 878 | Cry Me A River | ... Curseur interne 23:38:06 Programmation Web 2017-2018

Mode de récupération des résultats d'une requête $pdostat = $pdo->query( "SELECT id, name FROM artist"); $r = $pdostat->fetch(fetch_mode)); fetch_mode PDO::FETCH_NUM PDO::FETCH_ASSOC PDO::FETCH_OBJ PDO::FETCH_CLASS, ma_classe // array $r[0] $r[1] // ma_classe $r->id $r->name // object $r->id $r->name // array $r['id'] $r['name'] 23:38:06 Programmation Web 2017-2018

Exploitation des résultats d'une requête (1) try { $pdo=new PDO("mysql:host=dbs;dbname=music"); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->query("SELECT id, name FROM artist"); $pdostat->setFetchMode(PDO::FETCH_ASSOC); foreach ($pdostat as $ligne) { echo "<p>" . $ligne['name'] . "\n"; } catch (Exception $e) { echo "<p>ERREUR : ".$e->getMessage(); 23:38:06 Programmation Web 2017-2018

Exploitation des résultats d'une requête (2) try { $pdo=new PDO("mysql:host=dbs;dbname=music"); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->query("SELECT id, name FROM artist"); foreach ($pdostat->fetchAll(PDO::FETCH_ASSOC) as $ligne) { echo "<p>" . $ligne['name'] . "\n"; } catch (Exception $e) { echo "<p>ERREUR : ".$e->getMessage(); 23:38:06 Programmation Web 2017-2018

Exploitation des résultats d'une requête (3) try { $pdo=new PDO("mysql:host=localhost;dbname=mysql") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->query("SELECT name FROM user") ; while (($ligne = $pdostat->fetch(PDO::FETCH_ASSOC)) !== false) { echo "<p>" . $ligne['name'] . "\n" ; } catch (Exception $e) { echo "<p>ERREUR : ".$e->getMessage() ; 23:38:06 Programmation Web 2017-2018

Modes de récupération des données (1) PDO::FETCH_ASSOC retourner chaque ligne dans un tableau indexé par les noms des colonnes comme elles sont retournées dans le jeu de résultats correspondant. Si le jeu de résultats contient de multiples colonnes avec le même nom, PDO::FETCH_ASSOC retourne une seule valeur par nom de colonne. PDO::FETCH_NUM retourner chaque ligne dans un tableau indexé par le numéro des colonnes comme elles sont retournées dans le jeu de résultats correspondant, en commençant à 0. 23:38:06 Programmation Web 2017-2018

Modes de récupération des données (2) PDO::FETCH_BOTH (par défaut) retourner chaque ligne dans un tableau indexé par les noms des colonnes ainsi que leurs numéros, comme elles sont retournées dans le jeu de résultats correspondant, en commençant à 0. PDO::FETCH_OBJ retourner chaque ligne dans un objet avec les noms de propriétés correspondant aux noms des colonnes comme elles sont retournées dans le jeu de résultats. 23:38:06 Programmation Web 2017-2018

Modes de récupération des données (3) PDO::FETCH_BOUND retourner true et assigner les valeurs des colonnes du jeu de résultats dans les variables PHP auxquelles elles sont liées avec la méthode PDOStatement::bindParam() ou la méthode PDOStatement::bindColumn(). PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE retourner une nouvelle instance de la classe demandée, liant les colonnes aux propriétés nommées dans la classe. Nom de la classe = 1ère colonne. 23:38:06 Programmation Web 2017-2018

Modes de récupération des données (4) PDO::FETCH_INTO met à jour une instance existante de la classe demandée, liant les colonnes du jeu de résultats aux noms des propriétés de la classe. PDO::FETCH_LAZY retourner chaque ligne en tant qu'objet avec les noms des attributs correspondant aux noms des colonnes retournées dans le jeu de résultats. PDO::FETCH_LAZY crée les noms des attributs de l'objet comme ils sont rencontrés. 23:38:06 Programmation Web 2017-2018

Exemple avec PDO::FETCH_CLASS $stmt = $pdo->query(<<<SQL SELECT id, name FROM artist WHERE id = 12 SQL ) ; $stmt->setFetchMode(PDO::FETCH_CLASS, 'Artist') ; if (($object = $stmt->fetch()) !== false) { return $object ; } Instancie un objet de la classe Artist dont les attributs sont supposés être id et name, exactement le nom des champs de la table 23:38:06 Programmation Web 2017-2018

Requêtes préparées 23:38:06 Programmation Web 2017-2018

Préparation d'une requête Déroulement d'une requête SQL Analyse Compilation Optimisation Exécution Exécution répétée d'une requête : 1+2+3+4 Préparation d'une requête : 1+2+3 Exécution répétée d'une requête préparée : 4 Préparation en fonction de paramètres : Anonymes Nommés 23:38:06 Programmation Web 2017-2018

Préparation d'une requête PDO::prepare(string statement [,array driver_options]): PDOStatement statement : la requête à préparer. Peut contenir des paramètres anonymes (?) ou nommés (:nom) driver_options : tableau d'options du driver retourne un objet PDOStatement qui effectuera l'association des paramètres et exécutera la requête $pdo = new PDO("mysql:host=localhost;dbname=mysql"); $pdostat = $pdo->prepare("SELECT id, name FROM artist WHERE id = ?"); 23:38:06 Programmation Web 2017-2018

Association des paramètres d'une requête PDOStatement::bindValue(mixed parameter, mixed value [, int data_type]): bool parameter : le paramètre (nom ou position [1…n]) value : sa valeur data_type : le type de la valeur PDO::PARAM_BOOL booléen. PDO::PARAM_NULL NULL SQL. PDO::PARAM_INT INTEGER SQL. PDO::PARAM_STR CHAR, VARCHAR ou autre chaîne. PDO::PARAM_LOB "objet large" SQL. PDOStatement::execute([array parameters]): bool parameters : tableau associatif ou indexé des valeurs 23:38:06 Programmation Web 2017-2018

Préparation puis exécution d'une requête (1) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->prepare("SELECT * FROM user WHERE login = ?") ; $pdostat->bindValue(1, 'root') ; $pdostat->execute() ; // Utilisation du résultat $pdostat->bindValue(1, 'cutrona') ; paramètre anonyme Association d'une valeur au 1er paramètre Exécution de la requête Exécution de la requête Préparation de la requête Association d'une valeur au 1er paramètre 23:38:06 Programmation Web 2017-2018

Préparation puis exécution d'une requête (2) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->prepare("SELECT * FROM user WHERE login = :utilisateur") ; $pdostat->bindValue(':utilisateur', 'root') ; $pdostat->execute() ; // Utilisation du résultat $pdostat->bindValue(':utilisateur', 'cutrona') ; paramètre nommé Association d'une valeur au paramètre nommé Exécution de la requête Exécution de la requête Préparation de la requête Association d'une valeur au paramètre nommé 23:38:06 Programmation Web 2017-2018

Préparation puis exécution d'une requête (3) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->prepare("SELECT * FROM user WHERE login = ?") ; $pdostat->execute(array('root')) ; // Utilisation du résultat $pdostat->execute(array('cutrona')) ; paramètre anonyme Association d'une valeur au 1er paramètre Exécution de la requête Exécution de la requête Préparation de la requête Association d'une valeur au 1er paramètre 23:38:06 Programmation Web 2017-2018

Préparation puis exécution d'une requête (4) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdostat = $pdo->prepare("SELECT * FROM user WHERE login = :utilisateur") ; $pdostat->execute( array(':utilisateur' => 'root')) ; // Utilisation du résultat array(':utilisateur' => 'cutrona')) ; paramètre nommé Association d'une valeur au paramètre nommé Exécution de la requête Exécution de la requête Préparation de la requête Association d'une valeur au paramètre nommé 23:38:06 Programmation Web 2017-2018

Intérêt des requêtes préparées Amélioration des performances en cas d'exécutions répétées Émulation faite par PDO si le driver ne les supporte pas nativement Protection automatique des valeurs des paramètres pour interdire les attaques par injection de code SQL 23:38:06 Programmation Web 2017-2018

Attaque par injection SQL 23:38:06 Programmation Web 2017-2018

Attaque par injection SQL ? Exemple : validation d'un login/password sur un site Requête consistant à trouver un enregistrement correspondant au couple login/password fourni par l'utilisateur Requête naïve : SELECT * FROM membre WHERE login='{$_POST['login']}' AND passwd='{$_POST['passwd']}' Et si on essayait de fournir un mot de passe un peu particulier… 23:38:06 Programmation Web 2017-2018

Exemple concret d'injection SQL (1) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdostat = $pdo->query($req = <<<SQL SELECT * FROM membre WHERE login = '{$_POST['login']}' AND passwd = '{$_POST['passwd']}' SQL ) ; echo "Requête:\n$req\n" ; if (($utilisateur = $pdostat->fetch())) !== false) echo "Bienvenue {$utilisateur['nom']}" ; else echo "Désolé..." ; 23:38:06 Programmation Web 2017-2018

Exemple concret d'injection SQL (2) Saisie de l'utilisateur par formulaire : login : whatever pass : who_cares? Requête: SELECT * FROM membre WHERE login='whatever' AND passwd='who_cares?' Désolé... 23:38:06 Programmation Web 2017-2018

Exemple concret d'injection SQL (3) Saisie de l'utilisateur : login : whatever pass : who_cares?' OR true!=' Le premier membre est certainement l’administrateur ! Donne toutes les lignes de la table membre Requête: SELECT * FROM membre WHERE login='whatever' AND passwd='who_cares?' OR true!='' Bienvenue John 23:38:06 Programmation Web 2017-2018

Protection contre les injections SQL (1) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $pdostat = $pdo->prepare($req = <<<SQL SELECT * FROM membre WHERE login = ? AND passwd = ? SQL ) ; $pdostat->execute(array($_POST['login'], $_POST['passwd'])) ; if (($utilisateur = $pdostat->fetch()) !== false) { echo "Bienvenue {$utilisateur['nom']}\n" ; } else { echo "Désole...\n" ; } 23:38:06 Programmation Web 2017-2018

Protection contre les injections SQL (2) $pdo = new PDO("mysql:host=dbs;dbname=app") ; $login = $pdo->quote($_POST['login']) ; $passwd = $pdo->quote($_POST['passwd']) ; $pdostat = $pdo->query($req = <<<SQL SELECT * FROM membre WHERE login = $login AND passwd = $passwd SQL ) ; echo "Requête:\n$req\n" ; if (($utilisateur = $pdostat->fetch() !== false) { echo "Bienvenue {$utilisateur['nom']}\n" ; } else { echo "Désole...\n" ; } Requête: SELECT * FROM membre WHERE login='whatever' AND passwd='who_cares?\' OR true!=\'' Désolé... 23:38:06 Programmation Web 2017-2018

Protection contre les injections SQL : conclusion Jamais de substitution de variable dans une requête SQL Jamais de concaténation dans une requête SQL Préparer une requête = augmenter son efficacité (cache) Préparer une requête = pouvoir la paramétrer Paramétrer une requête = se protéger contre l’injection de code 23:38:06 Programmation Web 2017-2018

Gestion des transactions 23:38:06 Programmation Web 2017-2018

Transactions Transactions : Mode PDO par défaut : Atomicité, Consistance, Isolation et Durabilité BEGIN puis COMMIT ou ROLLBACK Mode PDO par défaut : Chaque requête est validée automatiquement PDO::beginTransaction() PDO::commit() PDO::rollBack() Tous les moteurs ne supportent pas les transactions  PDOException 23:38:06 Programmation Web 2017-2018