Les fetch modes de PDO 2 : les modes orientés objet

Suite de ma série consacrée aux modes de récupération de PDO, voici les modes qui permettent de travailler avec des objets.

FETCH_OBJ

Le tableau retourné contient les résultats sous forme d’instances de stdClass, contenant les valeurs des colonnes comme des variables publiques. Ce mode ne permet pas de récupérer plusieurs colonnes avec le même nom (voir FETCH_ASSOC).

Exemple :

Array
(
  [0] => stdClass Object
    (
      [id] => 1
      [login] => toto
    )

  [1] => stdClass Object
    (
      [id] => 2
      [login] => titi
    )

)

Par rapport à FETCH_BOTH :

  • Temps de récupération : +7,6% (à noter qu’avec while + fetch, ça passe à +30%)
  • Taille du résultat : -15%

Cette méthode est plus lente que la méthode par défaut (qui est déjà particulièrement lente), et honnetement, je ne vois pas l’avantage d’un objet par rapport à un tableau associatif renvoyé par FETCH_ASSOC

FETCH_CLASS

Ce mode est équivalent à FETCH_OBJ sauf qu’il permet d’utiliser une classe personnalisée pour stocker les résultats. Ce mode ne permet pas de récupérer plusieurs colonnes avec le même nom (voir FETCH_ASSOC).

Ce mode nécessite de passer, en plus du mode, des paramètres supplémentaires. D’après la documentation, il faudrait utiliser la fonction setFetchMode pour passer ces paramètres (ou directement au moment de faire query), mais en pratique, fetchAll les accepte aussi (alors qu’ils ne sont censés exister)… Par contre, fetch ne les accepte pas.

Le deuxième paramètre est donc le nom d’une classe. Si la classe contient déjà des variables correspondantes aux colonnes, PDO les rempli et se fiche pas mal de leur visibilité. Sinon, PDO crée les variables avec une visibilité publique. Un troisième paramètre (optionnel) permet de passer des paramètres au constructeur de la classe. A noter que quand le constructeur est appellé, les variables internes ont déjà une valeur (voir FETCH_PROPS_LATE dans le dernier article de cette série si ce comportement pose un problème).

Si la classe n’existe pas, le résultat est un tableau identique à celui obtenu avec FETCH_BOTH (si un autre mode par défaut est défini, il n’est pas pris en compte).

Exemple :

class MyClass
{
  function __construct()
  {
    var_dump(func_get_args()); // affiche 'foo' et 'bar'
    var_dump($this->id, $this->login); // affiche l'id et le login
  }
}

$results = $dbh->query($sql);
$results->fetchAll(PDO::FETCH_CLASS, 'MyClass', array('foo', 'bar'));
Array
(
  [0] => MyClass Object
    (
      [id] => 1
      [login] => toto
    )

  [1] => MyClass Object
    (
      [id] => 2
      [login] => titi
    )

)

Les performances dépendent évidemment de la taille de la classe : plus il y a d’infos dedans, pire c’est. Avec une classe entièrement vide (c’est-à-dire avec meilleures performances possibles), on obtient, par rapport à FETCH_BOTH :

  • Temps de récupération : +6% (à noter qu’avec while + fetch, ça passe à +32%)
  • Taille du résultat : dépend de la taille de la classe

En déclarant un constructeur (même vide), les performances se dégradent énormement parceque le constructeur est appellé à chaque ligne de résultat :

  • Temps de récupération : +55% (avec while + fetch, ça descend à +36%)
  • Taille du résultat : dépend de la taille de la classe

Pour faire court : les performances sont catastrophiques par rapport aux modes classiques. Il est peut-être possible d’utiliser ce mode pour créer un petit ORM, mais pour l’instant je ne vois pas bien l’utilité.

FETCH_INTO

Ce mode permet de récupérer les données *dans* une instance de classe déjà existante…

Pour utiliser ce mode, il faut passer en paramètre la variable qui contient l’instance à modifier. Comme pour FETCH_CLASS, d’après la documentation il faut utiliser la fonction setFetchMode (ou directement query) pour passer ce paramètre. Et cette fois, curieusement, ni fetchAll ni fetch ne les accepte (avec fetchAll on obtient l’erreur plutôt originale SQLSTATE[HY000]: General error: Extraneous additional parameters).

La seule manière est donc d’utiliser :

class MyClass { }
$toto = new MyClass();
$results->setFechMode(PDO::FETCH_INTO, $toto);
// fetch

Attention, contrairement à FETCH_CLASS, si vous déclarez dans la classe les variables correspondant au nom des colonnes en protected ou private, PDO va retourner une erreur : Fatal error: Cannot access protected (ou private) property MyClass::$id.

Avec fetchAll, on obtient un tableau contenant exactement la même instance de classe autant de fois qu’il y a de lignes dans la réponse… L’instance contient uniquement les valeurs de la dernière ligne. C’est donc totalement inexploitable. Exemple :

Array
(
  [0] => MyClass Object
    (
      [id] => 2
      [login] => toto
    )

  [1] => MyClass Object
    (
      [id] => 2
      [login] => toto
    )

)

Si on utilise fetch dans une boucle while, fetch retourne à chaque étape une référence vers l’instance. Exemple :

while ( $r = $results->fetch() )
    var_dump($r == $toto); // affiche true

On peut donc utiliser :

while ( $results->fetch() )
    print_r($toto);
MyClass Object
(
  [id] => 1
  [login] => toto
)
MyClass Object
(
  [id] => 2
  [login] => titi
)

J’ai estimé les performances avec fetchAll afin de pouvoir comparer les résultats, mais il est important de noter qu’avec ce mode fetchAll ne sert à rien… Par rapport à FETCH_BOTH, on obtient :

  • Temps de récupération : -3,5% (avec while + fetch ça descend à -9%)
  • Taille du résultat : dépend de la taille de la classe

Le temps de récupération est plus court que FETCH_CLASS ou FETCH_OBJ car l’objet est déjà instancié. Mais, même si les performances sont correctes, pour l’instant je n’ai pas encore trouvé d’utilisation concrète pour ce mode.

A suivre : les modes spéciaux et les modes modificateurs.

One thought on “Les fetch modes de PDO 2 : les modes orientés objet

  1. Pingback: Tilàcica » Blog Archive » 10 bonnes raisons d’utiliser PDO (PHP Data Object)

Comments are closed.