Introduction
- Ma requête ne fonctionne pas, et pourtant je n'ai aucun message d'erreur !
- J'ai une erreur "Call to a member function fetch() on a non-object". Je ne comprends pas, pourtant j'appelle la méthode comme dans le tuto !
Hé oui ! Il faut savoir que par défaut, PDO n'affiche pas les erreurs SQL. Or, en tant que développeur, vous devez
aimer les messages d'erreurs ! Si, si ! Sans eux, on n'a aucune idée de l'endroit où l'on s'est trompé. Parfois même, ils nous disent ce qui ne va pas.
La preuve qu'ils sont utiles : PDO n'affiche pas les erreurs, et vous êtes perdu.
Des erreurs mystérieuses
Bon, j'ai compris pourquoi quand je faisais une erreur SQL, je n'avais pas de message d'erreur. Mais pourquoi est-ce que parfois j'ai une erreur sur le fetch(), alors ?
Hé bien tout d'abord, il faut se souvenir que l'on travaille avec deux langages :
Or, PDO ne vous montre pas les erreurs SQL. Les erreurs PHP ne sont pas influencées par PDO, elles sont influencées par la configuration de PHP.
Autrement dit, l'erreur que vous pouvez voir sur le fetch() est une erreur PHP causée indirectement
par une erreur SQL que vous ne voyez pas.
Prenons un code de base de sélection de données avec PDO :
Code : PHP | <?php
$stmt = $pdo->query('SELECT id, auteur, contenu, date FROM messages');
while($message = $stmt->fetch())
{
// Utilisation des données.
}
|
Admettons que, pour une raison ou pour une autre, ma requête échoue. Que va-t-il se passer ? Regardons la documentation de la méthode
PDO::query().
Citation : php.netPDO::query() retourne un objet PDOStatement, ou FALSE si une erreur survient.
Un objet de
la classe PDOStatement ? Hé oui, si vous avez la curiosité de jeter un œil à la documentation de cette classe, vous vous apercevrez que c'est cette classe qui possède la méthode fetch(). Si ma requête réussit,
$stmt sera donc une instance de la classe PDOStatement. Mais si elle échoue, elle contiendra uniquement…
FALSE !
Vous avez compris ? Si la requête échoue, il n'y a pas d'objet PDOStatement (pas d'objet du tout même, on obtient un booléen !). Sans objet, pas de méthode. Par conséquent, PHP nous prévient qu'on essaie d'appeler une méthode sur quelque chose qui n'est pas un objet, et que c'est donc impossible !
Et pourquoi aucun message d'erreur n'est affiché parfois ?
Tout simplement parce que dans le cas d'une requête de type
UPDATE ou
DELETE, on n'utilise pas (ou rarement) la valeur de retour de la méthode.
Vous l'aurez compris, il devient
urgent de demander poliment à PDO de nous prévenir en cas de problème.
Afficher les erreurs SQL
Pour modifier les paramètres de PDO, nous avons deux solutions. Soit on donne les options directement au constructeur, soit on utilise la méthode
PDO::setAttribute(). Je vais vous montrer ces deux méthodes, vous choisirez celle qui vous fait plaisir.
Il faut également savoir que l'on peut demander à PDO de nous avertir de deux façons :
- avec une erreur type "WARNING" ;
- avec une exception.
Vous connaissez déjà les erreurs WARNING. Un exemple ? Vous y avez droit si vous oubliez de donner un paramètre à une fonction : "
Warning: Wrong parameter count for intval()". Si vous choisissez ce mode, vous aurez une erreur de ce type en cas d'erreur SQL.
En ce qui concerne les exceptions, vous en avez déjà vu au moins une ! Souvenez-vous, quand vous construisiez votre objet PDO :
Citation : Cours de PHPCode : PHP | <?php
try
{
$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch (Exception $e)
{
die('Erreur : ' . $e->getMessage());
}
|
Ici,
$e est une exception. Les exceptions sont lancées par l'instruction
throw et peuvent être interceptées par un bloc catch(). Je ne vous en dis pas plus, je ne compte pas vous faire un cours sur les exceptions. Si vous êtes curieux, vous chercherez de votre côté.
Demander un WARNING
Sans plus attendre, voici comment demander à PDO d'afficher des erreurs WARNING en cas de problème.
Vous pouvez passer un array en quatrième paramètre du constructeur. Cet array devra avoir pour clé le nom de l'attribut à modifier, et pour valeur… la valeur de l'attribut à modifier. En ce qui nous concerne, l'attribut à modifier est PDO::ATTR_ERRMODE et sa valeur PDO::ERRMODE_WARNING. On le comprend très bien : ATTR mis pour "attribut", et ERRMODE pour "error mode".
Sinon, vous pouvez utiliser la méthode
PDO::setAttribute(). Elle prend deux paramètres : l'attribut à modifier et sa nouvelle valeur.
Voyez plutôt :
Code : PHP | <?php
// Je donne les paramètres au constructeur :
$pdo = new PDO('mysql:host=localhost;dbname=DBNAME', 'root', '', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
|
Code : PHP | <?php
// Je construis mon objet, puis je donne le paramètre à PDO::setAttribute() :
$pdo = new PDO('mysql:host=localhost;dbname=DBNAME', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
|
Demander une EXCEPTION
Allez, je suis sûr que vous pouvez trouver tout seul en cherchant la bonne valeur de l'attribut dans
la liste des constantes prédéfinies de PDO.
Secret (cliquez pour afficher)PDO::ERRMODE_EXCEPTION ? Oui, bravo ! Regardez ce que ça donne :
Code : PHP | <?php
// Je donne les paramètres au constructeur :
$pdo = new PDO('mysql:host=localhost;dbname=DBNAME', 'root', '', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ));
|
Code : PHP | <?php
// Je construis mon objet, puis je donne le paramètre à PDO::setAttribute() :
$pdo = new PDO('mysql:host=localhost;dbname=DBNAME', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
|
Et voilà, vos erreurs ne sont plus silencieuses ! Si vous choisissez les exceptions, mettez vos requêtes dans un bloc try/catch pour pouvoir les attraper. Un exemple ? Le voici :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | <?php
try
{
$pdo = new PDO('mysql:host=localhost;dbname=DBNAME', 'root', '', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ));
$stmt = $pdo->query('SELECT id, auteur, contenu, DATE_FORMAT(date, "%W %d %M %Y à %Hh%i") as date FROM messages');
$messages = $stmt->fetchAll(PDO::FETCH_OBJ);
}
catch(Exception $e)
{
exit('<b>Catched exception at line '. $e->getLine() .' :</b> '. $e->getMessage());
}
foreach($messages as $message)
{
echo '<p>Le ', $message->date, ' par ', $message->auteur, ' : <br />', $message->contenu, '</p>';
}
|
D'autres attributs à modifier !
Si vous regardez plus en détail
la liste des constantes de PDO que je vous ai montrée tout à l'heure, vous vous apercevrez que ERRMODE n'est pas le seul attribut modifiable. Par exemple, j'aime bien modifier le DEFAULT_FETCH_MODE, plutôt que de le donner à chaque fois à mes méthodes PDOStatement::fetch() ou PDOStatement::fetchAll().
Si vous êtes attentif, vous remarquerez que toutes les constantes qui commencent par PDO::ATTR_ sont des attributs que l'on peut modifier. Par exemple pour le fetch mode par défaut : PDO::ATTR_DEFAULT_FETCH_MODE fera l'affaire. Les valeurs possibles pour cet attribut sont les constantes qui commencent par PDO::FETCH_.
En bonus, voici comment je construis mon propre objet PDO. À vous d'adapter ce code selon vos goûts et vos besoins :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | <?php
try
{
$db_config = array();
$db_config['SGBD'] = 'mysql';
$db_config['HOST'] = 'localhost';
$db_config['DB_NAME'] = 'tests';
$db_config['USER'] = 'root';
$db_config['PASSWORD'] = '';
$db_config['OPTIONS'] = array(
// Activation des exceptions PDO :
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// Change le fetch mode par défaut sur FETCH_ASSOC ( fetch() retournera un tableau associatif ) :
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$pdo = new PDO($db_config['SGBD'] .':host='. $db_config['HOST'] .';dbname='. $db_config['DB_NAME'],
$db_config['USER'],
$db_config['PASSWORD'],
$db_config['OPTIONS']);
unset($db_config);
}
catch(Exception $e)
{
trigger_error($e->getMessage(), E_USER_ERROR);
}
|