Mis à jour le 08/01/2018
  • 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Les objets en profondeur

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Je suis sûr qu'actuellement, vous pensez que lorsqu'on fait un $objet = new MaClasse;, la variable $objet contient l'objet que l'on vient de créer. Personne ne peut vous en vouloir puisque personne ne vous a dit que c'était faux. Et bien je vous le dis maintenant : comme nous le verrons dans ce chapitre, une telle variable ne contient pas l'objet à proprement parler ! Ceci explique ainsi quelques comportements bizarres que peut avoir PHP avec les objets. Nous allons ainsi parler de la dernière méthode magique que je vous avais volontairement cachée.

Une fois tout ceci expliqué, nous jouerons un peu avec nos objets en les parcourant, à peu près de la même façon qu'avec des tableaux.

Comme vous le verrez, les objets réservent bien des surprises !

Un objet, un identifiant

Je vais commencer cette partie en vous faisant une révélation : quand vous instanciez une classe, la variable stockant l'objet ne stocke en fait pas l'objet lui-même, mais un identifiant qui représente cet objet. C'est-à-dire qu'en faisant $objet = new Classe;,  $objet ne contient pas l'objet lui-même, mais son identifiant unique. C'est un peu comme quand vous enregistrez des informations dans une BDD : la plupart du temps, vous avez un champ "id" unique qui représente l'entrée. Quand vous faites une requête SQL, vous sélectionnez l'élément en fonction de son id. Et bien là, c'est pareil : quand vous accédez à un attribut ou à une méthode de l'objet, PHP regarde l'identifiant contenu dans la variable, va chercher l'objet correspondant et effectue le traitement nécessaire. Il est très important que vous compreniez cette idée, sinon vous allez être complètement perdus pour la suite du chapitre.

Nous avons donc vu que la variable $objet contenait l'identifiant de l'objet qu'elle a instancié. Vérifions cela :

<?php
class MaClasse
{
  public $attribut1;
  public $attribut2;
}

$a = new MaClasse;

$b = $a; // On assigne à $b l'identifiant de $a, donc $a et $b représentent le même objet.

$a->attribut1 = 'Hello';
echo $b->attribut1; // Affiche Hello.

$b->attribut2 = 'Salut';
echo $a->attribut2; // Affiche Salut.

Je commente plus en détail la ligne 10 pour ceux qui sont un peu perdus. Nous avons dit plus haut que $a ne contenait pas l'objet lui-même mais son identifiant (un identifiant d'objet). $a contient donc l'identifiant représentant l'objet créé. Ensuite, on assigne à $b la valeur de $a. Donc qu'est-ce que $b vaut maintenant ? Et bien la même chose que $a, à savoir l'identifiant qui représente l'objet ! $a et $b font donc référence à la même instance.

Schématiquement, on peut représenter le code ci-dessus comme ceci :

Exemple de conséquences des identifiants d'objet
Exemple de conséquences des identifiants d'objet

Comme vous le voyez sur l'image, en réalité, il n'y a qu'un seul objet, qu'un seul identifiant, mais deux variables contenant exactement le même identifiant d'objet. Tout ceci peut sembler abstrait, donc allez à votre rythme pour bien comprendre.

Maintenant que l'on sait que ces variables ne contiennent pas d'objet mais un identifiant d'objet, vous êtes censés savoir que lorsqu'un objet est passé en paramètre à une fonction ou renvoyé par une autre, on ne passe pas une copie de l'objet mais une copie de son identifiant ! Ainsi, vous n'êtes pas obligé de passer l'objet en référence, car vous passerez une référence de l'identifiant de l'objet. Inutile, donc.

Cependant un problème se pose. Comment faire pour copier un objet ? Comment faire pour pouvoir copier tous ses attributs et valeurs dans un nouvel objet unique ? On a vu qu'on ne pouvait pas faire un simple $objet1 = $objet2 pour arriver à cela. Comme vous vous en doutez peut-être, c'est là qu'intervient le clonage d'objet.

Pour cloner un objet, c'est assez simple. Il faut utiliser le mot-clé clone juste avant l'objet à copier. Exemple :

<?php
$copie = clone $origine; // On copie le contenu de l'objet $origine dans l'objet $copie.

C'est aussi simple que cela. Ainsi les deux objets contiennent des identifiants différents : par conséquent, si on veut modifier l'un d'eux, on peut le faire sans qu'aucune propriété de l'autre ne soit modifiée.

Il n'était pas question d'une méthode magique ?

Si si, j'y viens. :)

Lorsque vous clonez un objet, la méthode __clone du nouvel objet sera appelée (du moins, si vous l'avez définie). Vous ne pouvez pas appeler cette méthode directement. C'est la méthode __clone du nouvel objet créé qui est appelée, pas la méthode __clone de l'objet à cloner.

Vous pouvez utiliser cette méthode pour modifier certains attributs pour le nouvel objet, ou alors incrémenter un compteur d'instances par exemple.

<?php
class MaClasse
{
  private static $instances = 0;
  
  public function __construct()
  {
    self::$instances++;
  }
  
  public function __clone()
  {
    self::$instances++;
  }
  
  public static function getInstances()
  {
    return self::$instances;
  }
}

$a = new MaClasse;
$b = clone $a;

echo 'Nombre d\'instances de MaClasse : ', MaClasse::getInstances();

Ce qui affichera :

Résultat affiché par le script
Résultat affiché par le script

Comparons nos objets

Nous allons maintenant voir comment comparer deux objets. C'est très simple, il suffit de faire comme vous avez toujours fait en comparant des chaînes de caractères ou des nombres. Voici un exemple :

<?php
if ($objet1 == $objet2)
{
  echo '$objet1 et $objet2 sont identiques !';
}
else
{
  echo '$objet1 et $objet2 sont différents !';
}

Cette partie ne vous expliquera donc pas comment comparer des objets mais la démarche que PHP exécute pour les comparer et les effets que ces comparaisons peuvent produire.

Reprenons le code ci-dessus. Pour que la condition renvoie true, il faut que $objet1 et $objet2 aient les mêmes attributs et les mêmes valeurs, mais également que les deux objets soient des instances de la même classe. C'est-à-dire que même s'ils ont les mêmes attributs et valeurs mais que l'un est une instance de la classe A et l'autre une instance de la classe B, la condition renverra false.

Exemple :

<?php
class A
{
  public $attribut1;
  public $attribut2;
}

class B
{
  public $attribut1;
  public $attribut2;
}

$a = new A;
$a->attribut1 = 'Hello';
$a->attribut2 = 'Salut';

$b = new B;
$b->attribut1 = 'Hello';
$b->attribut2 = 'Salut';

$c = new A;
$c->attribut1 = 'Hello';
$c->attribut2 = 'Salut';

if ($a == $b)
{
  echo '$a == $b';
}
else
{
  echo '$a != $b';
}

echo '<br />';

if ($a == $c)
{
  echo '$a == $c';
}
else
{
  echo '$a != $c';
}

Si vous avez bien suivi, vous savez ce qui va s'afficher, à savoir :

Résultat affiché par le script
Résultat affiché par le script

Comme on peut le voir, $a et $b ont beau avoir les mêmes attributs et les mêmes valeurs, ils ne sont pas identiques car ils ne sont pas des instances de la même classe. Par contre, $a et $c sont bien identiques.

Parlons maintenant de l'opérateur === qui permet de vérifier que deux objets sont strictement identiques. Vous n'avez jamais entendu parler de cet opérateur ? Allez lire ce tutoriel !

Cet opérateur vérifiera si les deux objets font référence vers la même instance. Il vérifiera donc que les deux identifiants d'objets comparés sont les mêmes. Allez relire la première partie de ce chapitre si vous êtes un peu perdu.

Faisons quelques tests pour être sûr que vous avez bien compris :

<?php
class A
{
  public $attribut1;
  public $attribut2;
}

$a = new A;
$a->attribut1 = 'Hello';
$a->attribut2 = 'Salut';

$b = new A;
$b->attribut1 = 'Hello';
$b->attribut2 = 'Salut';

$c = $a;

if ($a === $b)
{
  echo '$a === $b';
}
else
{
  echo '$a !== $b';
}

echo '<br />';

if ($a === $c)
{
  echo '$a === $c';
}
else
{
  echo '$a !== $c';
}

Et à l'écran s'affichera :

Résultat affiché par le script
Résultat affiché par le script

On voit donc que cette fois-ci, la condition qui renvoyait true avec l'opérateur == renvoie maintenant false. $a et $c font référence à la même instance, la condition renvoie donc true. ;)

Parcourons nos objets

Finissons en douceur en voyant comment parcourir nos objets et en quoi cela consiste.

Le fait de parcourir un objet consiste à lire tous les attributs visibles de l'objet. Qu'est-ce que cela veut dire ? Ceci veut tout simplement dire que vous ne pourrez pas lire les attributs privés ou protégés en dehors de la classe, mais l'inverse est tout à fait possible. Je ne vous apprends rien de nouveau me direz-vous, mais ce rappel me semblait important pour vous expliquer le parcours d'objets.

Qui dit "parcours" dit "boucle". Quelle boucle devrons-nous utiliser pour parcourir un objet ? Et bien la même boucle que pour parcourir un tableau... J'ai nommé foreach !

Son utilisation est d'une simplicité remarquable (du moins, si vous savez parcourir un tableau). Sa syntaxe est la même. Il y en a deux possibles :

  • foreach ($objet as $valeur) : $valeur sera la valeur de l'attribut actuellement lu.

  • foreach ($objet as $attribut => $valeur) : $attribut aura pour valeur le nom de l'attribut actuellement lu et $valeur sera sa valeur.

Vous ne devez sans doute pas être dépaysé, il n'y a presque rien de nouveau. Comme je vous l'ai dit, la boucle foreach parcourt les attributs visibles. Faisons quelques tests. Normalement, vous devez déjà anticiper le bon résultat (enfin, j'espère, mais si vous êtes tombé à côté de la plaque ce n'est pas un drame !)

<?php
class MaClasse
{
  public $attribut1 = 'Premier attribut public';
  public $attribut2 = 'Deuxième attribut public';
  
  protected $attributProtege1 = 'Premier attribut protégé';
  protected $attributProtege2 = 'Deuxième attribut protégé';
  
  private $attributPrive1 = 'Premier attribut privé';
  private $attributPrive2 = 'Deuxième attribut privé';
  
  function listeAttributs()
  {
    foreach ($this as $attribut => $valeur)
    {
      echo '<strong>', $attribut, '</strong> => ', $valeur, '<br />';
    }
  }
}

class Enfant extends MaClasse
{
  function listeAttributs() // Redéclaration de la fonction pour que ce ne soit pas celle de la classe mère qui soit appelée.
  {
    foreach ($this as $attribut => $valeur)
    {
      echo '<strong>', $attribut, '</strong> => ', $valeur, '<br />';
    }
  }
}

$classe = new MaClasse;
$enfant = new Enfant;

echo '---- Liste les attributs depuis l\'intérieur de la classe principale ----<br />';
$classe->listeAttributs();

echo '<br />---- Liste les attributs depuis l\'intérieur de la classe enfant ----<br />';
$enfant->listeAttributs();

echo '<br />---- Liste les attributs depuis le script global ----<br />';

foreach ($classe as $attribut => $valeur)
{
  echo '<strong>', $attribut, '</strong> => ', $valeur, '<br />';
}

Ce qui affichera :

Résultat affiché par le script
Résultat affiché par le script

J'ai volontairement terminé ce chapitre par le parcours d'objets. Pourquoi ? Car dans le prochain chapitre nous verrons comment modifier le comportement de l'objet quand il est parcouru grâce aux interfaces ! Celles-ci permettent de réaliser beaucoup de choses pratiques, mais je ne vous en dis pas plus.

En résumé

  • Une variable ne contient jamais d'objet à proprement parler, mais leurs identifiants.

  • Pour dupliquer un objet, l'opérateur = n'a donc pas l'effet désiré : il faut cloner l'objet grâce à l'opérateur clone.

  • Pour comparer deux objets, l'opérateur == vérifie que les deux objets sont issus de la même classe et que les valeurs de chaque attribut sont identiques, tandis que l'opérateur === vérifie que les deux identifiants d'objet sont les mêmes.

  • Il est possible de parcourir un objet grâce la structure foreach : ceci aura pour effet de lister tous les attributs auxquels la structure a accès (par exemple, si la structure est située à l'extérieur de la classe, seuls les attributs publics seront listés).

Exemple de certificat de réussite
Exemple de certificat de réussite