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 interfaces

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

Si, dans l'une de vos méthodes, on vous passe un objet quelconque, il vous est impossible de savoir si vous pouvez invoquer telle ou telle méthode sur ce dernier pour la simple et bonne raison que vous n'êtes pas totalement sûr que ces méthodes existent. En effet, si vous ne connaissez pas la classe dont l'objet est l'instance, vous ne pouvez pas vérifier l'existence de ces méthodes.

En PHP, il existe un moyen d'imposer une structure à nos classes, c'est-à-dire d'obliger certaines classes à implémenter certaines méthodes. Pour y arriver, nous allons nous servir des interfaces. Une fois toute la théorie posée, nous allons nous servir d'interfaces pré-définies afin de jouer un petit peu avec nos objets.

Présentation et création d'interfaces

Le rôle d'une interface

Techniquement, une interface est une classe entièrement abstraite. Son rôle est de décrire un comportement à notre objet. Les interfaces ne doivent pas être confondues avec l'héritage : l'héritage représente un sous-ensemble (exemple : un magicien est un sous-ensemble d'un personnage). Ainsi, une voiture et un personnage n'ont aucune raison d'hériter d'une même classe. Par contre, une voiture et un personnage peuvent tous les deux se déplacer, donc une interface représentant ce point commun pourra être créée.

Créer une interface

Une interface se déclare avec le mot-cléinterface, suivi du nom de l'interface, suivi d'une paire d'accolades. C'est entre ces accolades que vous listerez des méthodes. Par exemple, voici une interface pouvant représenter le point commun évoqué ci-dessus :

<?php
interface Movable
{
  public function move($dest);
}
  1. Toutes les méthodes présentes dans une interface doivent être publiques.

  2. Une interface ne peut pas lister de méthodes abstraites ou finales.

  3. Une interface ne peut pas avoir le même nom qu'une classe et vice-versa.

Implémenter une interface

Cette interface étant toute seule, elle est un peu inutile. Il va donc falloir implémenter l'interface à notre classe grâce au mot-cléimplements! La démarche à exécuter est comme quand on faisait hériter une classe d'une autre, à savoir :

<?php
class Personnage implements Movable
{

}

Essayez ce code et... observez le résultat :

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

Et oui, une erreur fatale est générée car notre classePersonnagen'a pas implémenté la méthode présente dans l'interfaceMovable. Pour que ce code ne génère aucune erreur, il faut qu'il y ait au minimum ce code :

<?php
class Personnage implements Movable
{
  public function move($dest)
  {
  
  }
}

Et là... l'erreur a disparu !

Si vous héritez une classe et que vous implémentez une interface, alors vous devez d'abord spécifier la classe à hériter avec le mot-cléextends puis les interfaces à implémenter avec le mot-cléimplements.

Vous pouvez très bien implémenter plus d'une interface par classe, à condition que celles-ci n'aient aucune méthode portant le même nom ! Exemple :

<?php
interface iA
{
  public function test1();
}

interface iB
{
  public function test2();
}

class A implements iA, iB
{
  // Pour ne générer aucune erreur, il va falloir écrire les méthodes de iA et de iB.
  
  public function test1()
  {
  
  }
  
  public function test2()
  {
  
  }
}

Les constantes d'interfaces

Les constantes d'interfaces fonctionnent exactement comme les constantes de classes. Elles ne peuvent être écrasées par des classes qui implémentent l'interface. Exemple :

<?php
interface iInterface
{
  const MA_CONSTANTE = 'Hello !';
}

echo iInterface::MA_CONSTANTE; // Affiche Hello !

class MaClasse implements iInterface
{

}

echo MaClasse::MA_CONSTANTE; // Affiche Hello !

Hériter ses interfaces

Comme pour les classes, vous pouvez hériter vos interfaces grâce à l'opérateurextends. Vous ne pouvez réécrire ni une méthode ni une constante qui a déjà été listée dans l'interface parente. Exemple :

<?php
interface iA
{
  public function test1();
}

interface iB extends iA
{
  public function test1 ($param1, $param2); // Erreur fatale : impossible de réécrire cette méthode.
}

interface iC extends iA
{
  public function test2();
}

class MaClasse implements iC
{
  // Pour ne générer aucune erreur, on doit écrire les méthodes de iC et aussi de iA.
  
  public function test1()
  {
  
  }
  
  public function test2()
  {
  
  }
}

Contrairement aux classes, les interfaces peuvent hériter de plusieurs interfaces à la fois. Il vous suffit de séparer leur nom par une virgule. Exemple :

<?php
interface iA
{
  public function test1();
}

interface iB
{
  public function test2();
}

interface iC extends iA, iB
{
  public function test3();
}

Dans cet exemple, si on imagine une classe implémentant iC, celle-ci devra implémenter les trois méthodestest1,test2ettest3.

Interfaces prédéfinies

Nous allons maintenant aborder les interfaces prédéfinies. Grâce à certaines, nous allons pouvoir modifier le comportement de nos objets ou réaliser plusieurs choses pratiques. Il y a beaucoup d'interfaces prédéfinies, je ne vous les présenterai pas toutes, seulement quatre d'entre elles. Déjà, avec celles-ci, nous allons pouvoir réaliser de belles choses, et puis vous êtes libres de lire la documentation pour découvrir toutes les interfaces. Nous allons ici créer un « tableau-objet ».

Définition d'un itérateur

Afin de comprendre un peu plus ce que l'on va faire, on va commencer par voir ce qu'est un itérateur. Un itérateur est un objet capable de parcourir un autre objet. Bien entendu, cet objet à parcourir doit pouvoir être parcouru (on dit alors qu'il doit être itératif). Pour ce faire, nous devons imposer un comportement à nos objets afin qu'ils puissent être parcourus. Vous voyez où je veux en venir ? Ce comportement à imposer se fera par le biais d'interfaces ! L'interface la plus basique pour rendre un objet itératif est Iterator .

L'interfaceIterator

Commençons d'abord par l'interfaceIterator. Si votre classe implémente cette interface, alors vous pourrez modifier le comportement de votre objet lorsqu'il est parcouru. Cette interface comporte 5 méthodes :

  • current: renvoie l'élément courant ;

  • key: retourne la clé de l'élément courant ;

  • next: déplace le pointeur sur l'élément suivant ;

  • rewind: remet le pointeur sur le premier élément ;

  • valid: vérifie si la position courante est valide.

En écrivant ces méthodes, on pourra renvoyer la valeur qu'on veut, et pas forcément la valeur de l'attribut actuellement lu. Imaginons qu'on ait un attribut qui soit un tableau. On pourrait très bien créer un petit script qui, au lieu de parcourir l'objet, parcourt le tableau ! Je vous laisse essayer. Vous aurez besoin d'un attribut$positionqui stocke la position actuelle.

Correction :

<?php
class MaClasse implements Iterator
{
  private $position = 0;
  private $tableau = ['Premier élément', 'Deuxième élément', 'Troisième élément', 'Quatrième élément', 'Cinquième élément'];
  
  /**
   * Retourne l'élément courant du tableau.
   */
  public function current()
  {
    return $this->tableau[$this->position];
  }
  
  /**
   * Retourne la clé actuelle (c'est la même que la position dans notre cas).
   */
  public function key()
  {
    return $this->position;
  }
  
  /**
   * Déplace le curseur vers l'élément suivant.
   */
  public function next()
  {
    $this->position++;
  }
  
  /**
   * Remet la position du curseur à 0.
   */
  public function rewind()
  {
    $this->position = 0;
  }
  
  /**
   * Permet de tester si la position actuelle est valide.
   */
  public function valid()
  {
    return isset($this->tableau[$this->position]);
  }
}

$objet = new MaClasse;

foreach ($objet as $key => $value)
{
  echo $key, ' => ', $value, '<br />';
}

Ce qui affichera :

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

L'interfaceSeekableIterator

 

Cette interface hérite de l'interfaceIterator, on n'aura donc pas besoin d'implémenter les deux à notre classe.

SeekableIteratorajoute une méthode à la liste des méthodes d'Iterator: la méthodeseek. Cette méthode permet de placer le curseur interne à une position précise. Elle demande donc un argument : la position du curseur à laquelle il faut le placer. Je vous déconseille de modifier directement l'attribut$positionafin d'assigner directement la valeur de l'argument à$position. En effet, qui vous dit que la valeur de l'argument est une position valide ?

Je vous laisse réfléchir quant à l'implémentation de cette méthode. Voici la correction (j'ai repris la dernière classe) :

<?php
class MaClasse implements SeekableIterator
{
  private $position = 0;
  private $tableau = ['Premier élément', 'Deuxième élément', 'Troisième élément', 'Quatrième élément', 'Cinquième élément'];
  
  /**
   * Retourne l'élément courant du tableau.
   */
  public function current()
  {
    return $this->tableau[$this->position];
  }
  
  /**
   * Retourne la clé actuelle (c'est la même que la position dans notre cas).
   */
  public function key()
  {
    return $this->position;
  }
  
  /**
   * Déplace le curseur vers l'élément suivant.
   */
  public function next()
  {
    $this->position++;
  }
  
  /**
   * Remet la position du curseur à 0.
   */
  public function rewind()
  {
    $this->position = 0;
  }
  
  /**
   * Déplace le curseur interne.
   */
  public function seek($position)
  {
    $anciennePosition = $this->position;
    $this->position = $position;
    
    if (!$this->valid())
    {
      trigger_error('La position spécifiée n\'est pas valide', E_USER_WARNING);
      $this->position = $anciennePosition;
    }
  }
  
  /**
   * Permet de tester si la position actuelle est valide.
   */
  public function valid()
  {
    return isset($this->tableau[$this->position]);
  }
}

$objet = new MaClasse;

foreach ($objet as $key => $value)
{
  echo $key, ' => ', $value, '<br />';
}

$objet->seek(2);
echo '<br />', $objet->current();

Ce qui affichera :

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

L'interfaceArrayAccess

Nous allons enfin, grâce à cette interface, pouvoir placer des crochets à la suite de notre objet avec la clé à laquelle accéder, comme sur un vrai tableau ! L'interfaceArrayAccessliste quatre méthodes :

  • offsetExists: méthode qui vérifiera l'existence de la clé entre crochets lorsque l'objet est passé à la fonctionissetouempty(cette valeur entre crochet est passée à la méthode en paramètre) ;

  • offsetGet: méthode appelée lorsqu'on fait un simple$obj['clé']. La valeur 'clé' est donc passée à la méthodeoffsetGet;

  • offsetSet: méthode appelée lorsqu'on assigne une valeur à une entrée. Cette méthode reçoit donc deux arguments, la valeur de la clé et la valeur qu'on veut lui assigner.

  • offsetUnset: méthode appelée lorsqu'on appelle la fonctionunsetsur l'objet avec une valeur entre crochets. Cette méthode reçoit un argument, la valeur qui est mise entre les crochets.

Maintenant, votre mission est d'implémenter cette interface et de gérer l'attribut$tableaugrâce aux quatre méthodes. C'est parti !

Correction :

<?php
class MaClasse implements SeekableIterator, ArrayAccess
{
  private $position = 0;
  private $tableau = ['Premier élément', 'Deuxième élément', 'Troisième élément', 'Quatrième élément', 'Cinquième élément'];
  
  
  /* MÉTHODES DE L'INTERFACE SeekableIterator */
  
  
  /**
   * Retourne l'élément courant du tableau.
   */
  public function current()
  {
    return $this->tableau[$this->position];
  }
  
  /**
   * Retourne la clé actuelle (c'est la même que la position dans notre cas).
   */
  public function key()
  {
    return $this->position;
  }
  
  /**
   * Déplace le curseur vers l'élément suivant.
   */
  public function next()
  {
    $this->position++;
  }
  
  /**
   * Remet la position du curseur à 0.
   */
  public function rewind()
  {
    $this->position = 0;
  }
  
  /**
   * Déplace le curseur interne.
   */
  public function seek($position)
  {
    $anciennePosition = $this->position;
    $this->position = $position;
    
    if (!$this->valid())
    {
      trigger_error('La position spécifiée n\'est pas valide', E_USER_WARNING);
      $this->position = $anciennePosition;
    }
  }
  
  /**
   * Permet de tester si la position actuelle est valide.
   */
  public function valid()
  {
    return isset($this->tableau[$this->position]);
  }
  
  
  /* MÉTHODES DE L'INTERFACE ArrayAccess */
  
  
  /**
   * Vérifie si la clé existe.
   */
  public function offsetExists($key)
  {
    return isset($this->tableau[$key]);
  }
  
  /**
   * Retourne la valeur de la clé demandée.
   * Une notice sera émise si la clé n'existe pas, comme pour les vrais tableaux.
   */
  public function offsetGet($key)
  {
    return $this->tableau[$key];
  }
  
  /**
   * Assigne une valeur à une entrée.
   */
  public function offsetSet($key, $value)
  {
    $this->tableau[$key] = $value;
  }
  
  /**
   * Supprime une entrée et émettra une erreur si elle n'existe pas, comme pour les vrais tableaux.
   */
  public function offsetUnset($key)
  {
    unset($this->tableau[$key]);
  }
}

$objet = new MaClasse;

echo 'Parcours de l\'objet...<br />';
foreach ($objet as $key => $value)
{
  echo $key, ' => ', $value, '<br />';
}

echo '<br />Remise du curseur en troisième position...<br />';
$objet->seek(2);
echo 'Élément courant : ', $objet->current(), '<br />';

echo '<br />Affichage du troisième élément : ', $objet[2], '<br />';
echo 'Modification du troisième élément... ';
$objet[2] = 'Hello world !';
echo 'Nouvelle valeur : ', $objet[2], '<br /><br />';

echo 'Destruction du quatrième élément...<br />';
unset($objet[3]);

if (isset($objet[3]))
{
  echo '$objet[3] existe toujours... Bizarre...';
}
else
{
  echo 'Tout se passe bien, $objet[3] n\'existe plus !';
}

Ce qui affiche :

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

Alors, on se rapproche vraiment du comportement d'un tableau, n'est-ce pas ? On peut faire tout ce qu'on veut, comme sur un tableau ! Enfin, il manque juste un petit quelque chose pour que ce soit absolument parfait...

L'interfaceCountable

Et voici la dernière interface que je vous présenterai. Elle contient une méthode : la méthodecount. Celle-ci doit obligatoirement renvoyer un entier qui sera la valeur renvoyée par la fonctioncountappelée sur notre objet. Cette méthode n'est pas bien compliquée à implémenter, il suffit juste de retourner le nombre d'entrées de notre tableau.

Correction :

<?php
class MaClasse implements SeekableIterator, ArrayAccess, Countable
{
  private $position = 0;
  private $tableau = ['Premier élément', 'Deuxième élément', 'Troisième élément', 'Quatrième élément', 'Cinquième élément'];
  
  
  /* MÉTHODES DE L'INTERFACE SeekableIterator */
  
  
  /**
   * Retourne l'élément courant du tableau.
   */
  public function current()
  {
    return $this->tableau[$this->position];
  }
  
  /**
   * Retourne la clé actuelle (c'est la même que la position dans notre cas).
   */
  public function key()
  {
    return $this->position;
  }
  
  /**
   * Déplace le curseur vers l'élément suivant.
   */
  public function next()
  {
    $this->position++;
  }
  
  /**
   * Remet la position du curseur à 0.
   */
  public function rewind()
  {
    $this->position = 0;
  }
  
  /**
   * Déplace le curseur interne.
   */
  public function seek($position)
  {
    $anciennePosition = $this->position;
    $this->position = $position;
    
    if (!$this->valid())
    {
      trigger_error('La position spécifiée n\'est pas valide', E_USER_WARNING);
      $this->position = $anciennePosition;
    }
  }
  
  /**
   * Permet de tester si la position actuelle est valide.
   */
  public function valid()
  {
    return isset($this->tableau[$this->position]);
  }
  
  
  /* MÉTHODES DE L'INTERFACE ArrayAccess */
  
  
  /**
   * Vérifie si la clé existe.
   */
  public function offsetExists($key)
  {
    return isset($this->tableau[$key]);
  }
  
  /**
   * Retourne la valeur de la clé demandée.
   * Une notice sera émise si la clé n'existe pas, comme pour les vrais tableaux.
   */
  public function offsetGet($key)
  {
    return $this->tableau[$key];
  }
  
  /**
   * Assigne une valeur à une entrée.
   */
  public function offsetSet($key, $value)
  {
    $this->tableau[$key] = $value;
  }
  
  /**
   * Supprime une entrée et émettra une erreur si elle n'existe pas, comme pour les vrais tableaux.
   */
  public function offsetUnset($key)
  {
    unset($this->tableau[$key]);
  }
  
  
  /* MÉTHODES DE L'INTERFACE Countable */
  
  
  /**
   * Retourne le nombre d'entrées de notre tableau.
   */
  public function count()
  {
    return count($this->tableau);
  }
}

$objet = new MaClasse;

echo 'Parcours de l\'objet...<br />';
foreach ($objet as $key => $value)
{
  echo $key, ' => ', $value, '<br />';
}

echo '<br />Remise du curseur en troisième position...<br />';
$objet->seek(2);
echo 'Élément courant : ', $objet->current(), '<br />';

echo '<br />Affichage du troisième élément : ', $objet[2], '<br />';
echo 'Modification du troisième élément... ';
$objet[2] = 'Hello world !';
echo 'Nouvelle valeur : ', $objet[2], '<br /><br />';

echo 'Actuellement, mon tableau comporte ', count($objet), ' entrées<br /><br />';

echo 'Destruction du quatrième élément...<br />';
unset($objet[3]);

if (isset($objet[3]))
{
  echo '$objet[3] existe toujours... Bizarre...';
}
else
{
  echo 'Tout se passe bien, $objet[3] n\'existe plus !';
}

echo '<br /><br />Maintenant, il n\'en comporte plus que ', count($objet), ' !';

Ce qui affichera :

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

Bonus : la classeArrayIterator

Je dois vous avouer quelque chose : la classe que nous venons de créer pour pouvoir créer des « objets-tableaux » existe déjà. En effet, PHP possède nativement une classe nomméeArrayIterator. Comme notre précédente classe, celle-ci implémente les quatre interfaces qu'on a vues.

Mais pourquoi tu nous as fais faire tout ça ?!

Pour vous faire pratiquer, tiens. :)

Sachez que réécrire des classes ou fonctions natives de PHP est un excellent exercice (et c'est valable pour tous les langages de programmation). Je ne vais pas m'attarder sur cette classe, étant donné qu'elle s'utilise exactement comme la nôtre. Elle possède les mêmes méthodes, à une différence près : cette classe implémente un constructeur qui accepte un tableau en guise d'argument. Comme vous l'aurez deviné, c'est ce tableau qui sera « transformé » en objet. Ainsi, si vous faites unecho $monInstanceArrayIterator['cle'], alors à l'écran s'affichera l'entrée qui a pour clécledu tableau passé en paramètre.

En résumé

  • Une interface représente un comportement.

  • Les interfaces ne sont autre que des classes 100% abstraites.

  • Une interface s'utilise dans une classe grâce au mot-cléimplements.

  • Il est possible d'hériter ses interfaces grâce au mot-cléextends.

  • Il existe tout un panel d'interfaces pré-définies vous permettant de réaliser tout un tas de fonctionnalités intéressantes, comme la création « d'objets-tableaux ».

Vous êtes demandeur d'emploi ?
Sans diplôme post-bac ?

Devenez Développeur web junior

Je postule
Formation
en ligne
Financée
à 100%
Exemple de certificat de réussite
Exemple de certificat de réussite