Skip to content

5. Les objets

5.1. Toute variable peut devenir un objet doté d'attributs (exemple_14)


<?php

// toute variable peut avoir des attributs par construction
$obj1->attr1 = "un";
$obj1->attr2 = 100;
// affiche l'objet
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// modifie l'objet
$obj1->attr2+=100;
// affiche l'objet
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// affecte la valeur de objet1 à objet2
$obj2 = $obj1;
// modifie obj2
$obj2->attr2 = 0;
// affiche les deux objets
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet2=[$obj2->attr1,$obj2->attr2]\n";
// affecte la référence de objet1 à objet3
$obj3 = &$obj1;
// modifie obj3
$obj3->attr2 = 10;
// affiche les deux objets
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet3=[$obj3->attr1,$obj3->attr2]\n";
// un objet est-il un dictionnaire ?
print count($obj1)."\n";
while (list($attribut, $valeur) = each($obj1))
  print "obj1[$attribut]=$valeur\n";
// fin
exit;
?>

Résultats :

objet1=[un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[un,10]
objet3=[un,10]
1
obj1[attr1]=un
obj1[attr2]=10

Commentaires

  • ligne 4 : la notation $obj->attr désigne l'attribut attr de la variable $obj. S'il n'existe pas il est créé, faisant ainsi de la variable $obj, un objet avec attributs.
  • ligne 13 : l'expression $obj2=$obj1, lorsque $obj1 est un objet, est une copie d'objets par référence. Ainsi $obj2 et $obj1 sont des références sur un même objet. L'objet lui-même peut être modifié par l'une ou l'autre des références.
  • ligne 20 : l'expression $obj3=&$obj1 affecte à $obj3 la référence (l'adresse) de la variable $obj1. Si $obj1 est un objet comme c'est le cas ici, l'expression précédente est équivalente à $obj3=$obj1.
  • lignes 28-29 : montrent qu'un objet peut être parcouru comme un dictionnaire. Les clés du dictionnaire sont les noms des attributs et les valeurs du dictionnaire, les valeurs de ces mêmes attributs.
  • ligne 27 : la fonction count peut être appliquée à un objet mais ne donne pas, comme on aurait pu s'y attendre, le nombre d'attributs. Donc un objet présente des similitudes avec un dictionnaire mais n'en est pas un.

5.2. Une classe Personne sans attributs déclarés (exemple_15)


<?php

class Personne {

  // attributs de la classe
  // non déclarés - peuvent être créés dynamiquement
  
  // méthode
  function identité() {
    // à priori, utilise des attributs inexistants
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// test
// les attributs sont publics et peuvent être créés dynamiquement
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// appel d'une méthode
print "personne=" . $p->identité() . "\n";
// fin
exit;
?>

Résultats :

personne=[Paul,Langevin,48]

Commentaires

  • lignes 3-13 : définissent une classe Personne. Une classe est un moule à partir duquel on crée des objets. Une classe est un ensemble regroupant des attributs et des fonctions appelées méthodes. Il n'y a pas obligation à déclarer les attributs.
  • lignes 9-12 : la méthode identité affiche la valeur de trois attributs non déclarés dans la classe. Le mot clé $this désigne l'objet auquel on applique la méthode.
  • ligne 17 : on crée un objet $p de type Personne. Le mot clé new sert à créer un nouvel objet. L'opération rend une référence sur l'objet créé (donc une adresse). Diverses écritures sont possibles : new Personne(), new Personne, new personne. Le nom de la classe est insensible à la casse.
  • lignes 18-20 : les trois attributs nécessaires à la méthode identité sont créés dans l'objet $p.
  • ligne 22 : la méthode identité de la classe Personne est appliquée sur l'objet $p. Dans le code (lignes 9-11) de la méthode identité, $this référence le même objet que $p.

5.3. La classe Personne avec attributs déclarés (exemple_16)


<?php

class Personne {

  // attributs de la classe
  var $prénom;
  var $nom;
  var $âge;

  // méthode
  function identité() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
// test
// les attributs sont publics
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// appel d'une méthode
print "personne=" . $p->identité() . "\n";
// fin
exit;
?>

Résultats :

personne=[Paul,Langevin,48]

Commentaires

  • lignes 6-8 : les attributs de la classe sont explicitement déclarés avec le mot clé var.

5.4. La classe Personne avec un constructeur (exemple_17)

Les exemples précédents montraient des classes Personne exotiques telles qu'on pouvait les trouver dans PHP 4. Il n'est pas conseillé de suivre ces exemples. Nous présentons maintenant une classe Personne correspondant aux bonnes pratiques de PHP 5 :


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;

// getters and setters
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    $this->prénom = $prénom;
  }

  public function setNom($nom) {
    $this->nom = $nom;
  }

  public function setAge($age) {
    $this->âge = $age;
  }

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

// méthode toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// test
// création d'un objet personne
$p = new Personne("Paul", "Langevin", 48);
// identité de cette personne
print "personne=$p\n";
// on change l'âge
$p->setAge(14);
// identité de la personne
print "personne=$p\n";
// fin
exit;
?>

Résultats :

personne=[Paul,Langevin,48]
personne=[Paul,Langevin,14]

Commentaires

  • lignes 3-48 : la classe Personne
  • lignes 6-8 : les attributs privés (private) de la classe. Ces attributs ne sont visibles qu'à l'intérieur de la classe. Les autres mots clés utilisables sont :
  • public : fait de l'attribut un attribut public visible de l'extérieur de la classe
  • protected : fait de l'attribut un attribut protégé visible de l'intérieur de la classe et des classes dérivées de celle-ci.
  • parce que les attributs sont privés, on ne peut y accéder de l'extérieur de la classe. On ne peut donc écrire le code suivant :
$p=new Personne() ;
$p->nom="Landru" ;

Ici, on est en-dehors de la classe Personne. Comme l'attribut nom est privé, la ligne 2 est incorrecte. Pour initialiser les champs privés de l'objet $p, il y a deux moyens :

  • (suite)
    • utiliser les méthodes publiques set et get (le nom de ces méthodes peut être quelconque) des lignes 11-33. On pourra alors écrire :
$p=new Personne() ;
$p->setNom("Landru") ;
  • (suite)
    • utiliser le constructeur des lignes 36-41. On écrira alors :
$p=new Personne("Michel","Landru",44) ;

L'écriture ci-dessus, appelle automatiquement la méthode de la classe Personne appelée __construct.

  • ligne 54 : cette ligne affiche la personne $p sous la forme d'une chaîne de caractères. Pour ce faire, la méthode de la classe Personne appelée __toString est utilisée.

5.5. La classe Personne avec contrôle de validité dans le constructeur (exemple_18)

Le constructeur d'une classe est le bon endroit pour vérifier que les valeurs d'initialisation de l'objet sont corrects. Mais un objet peut être également initialisé par ses méthodes set ou équivalentes. Pour éviter de mettre à deux endroits différents les mêmes vérifications, on mettra ces dernières dans les méthodes set. Si une valeur d'initialisation d'un objet se révèle incorrecte, on lancera une exception. Voici un exemple :


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;
  
// getters et setters
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    // le prénom doit être non vide
    if (!preg_match("/^\s*(.+?)\s*$/", $prénom, $champs)) {
      throw new Exception("Le prénom doit être non vide");
    } else {
      $this->prénom = $champs[1];
    }
  }

  public function setNom($nom) {
    // le nom doit être non vide
    if (!preg_match("/^\s*(.+?)\s*$/", $nom, $champs)) {
      throw new Exception("Le nom doit être non vide");
    } else {
      $this->nom = $champs[1];
    }
  }

  public function setAge($âge) {
    // l'âge doit être valide
    if (!preg_match("/^\s*(\d+)\s*$/", $âge, $champs)) {
      throw new Exception("L'âge doit être un entier positif ou nul");
    } else {
      $this->âge = $champs[1];
    }
  }

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // méthode toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// test
// création d'un objet Personne
$p = new Personne("Paul", "Langevin", 48);
// identité de cette personne
print "personne=$p\n";
// création d'un objet Personne erroné
try {
  $p = new Personne("xx", "yy", "zz");
} catch (Exception $e) {
  print $e->getMessage();
}
// fin
exit;
?>

Résultats :

personne=[Paul,Langevin,48]
L'âge doit être un entier positif ou nul

Commentaires

  • lignes 23-30 : initialisation de l'attribut prénom et vérification de la valeur d'initialisation
  • ligne 25 : utilisation d'une expression régulière. Le prénom est une suite d'un ou plusieurs caractères éventuellement suivi ou précédé d'espaces.
  • ligne 26 : si le nom est vide, on lance une exception sinon le prénom est mémorisé (ligne 28)
  • ligne 66 : utilisation du constructeur de la classe Personne
  • ligne 68 : utilisation de la méthode __toString de la classe Personne
  • lignes 70-74 : gestion de l'éventuelle exception lancée par le constructeur de la classe Personne.

5.6. Ajout d'une méthode faisant office de second constructeur (exemple_19)

En PHP 5, il n'est pas possible d'avoir plusieurs constructeurs avec des paramètres différents qui permettraient de construire un objet de diverses façons. On peut alors utiliser des méthodes faisant office de constructeur :


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;
  
// getters et setters
  public function getPrénom() {
    return $this->prénom;
  }

….

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // méthode
  public function initWithPersonne($p) {
    // initialise l'objet courant avec une personne $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // méthode toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// test
// création d'un objet Personne
$p = new Personne("Paul", "Langevin", 48);
// identité de cette personne
print "personne=$p\n";
// création d'une seconde personne
$p2 = new Personne("Laure", "Adeline", 67);
// initialisation de la première avec les valeurs de la seconde
$p->initWithPersonne($p2);
// vérification
print "personne=$p\n";
// fin
exit;
?>

Résultats :

personne=[Paul,Langevin,48]
personne=[Laure,Adeline,67]

Commentaires

  • lignes 26-29 : la méthode initWithPersonne permet d'affecter à l'objet courant les valeurs des attributs d'un autre objet Personne. Ici, elle fait appel au constructeur __construct mais ce n'est pas obligatoire.

5.7. Un tableau d'objets Personne (exemple_20)

L'exemple suivant montre qu'on peut avoir des tableaux d'objets.


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;
  
// getters et setters
  public function getPrénom() {
    return $this->prénom;
  }

...

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // méthode
  public function initWithPersonne($p) {
….
  }

  // méthode toString
  function __toString() {
    ...
  }

}

// test
// création d'un tableau d'objets Personne
$groupe = array(new Personne("Paul", "Langevin", 48), new Personne("Sylvie", "Lefur", 70));

// identité de ces personnes
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// fin
exit;
?>

Résultats :

groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]

Commentaires

  • ligne 39 : création d'un tableau de 2 personnes
  • ligne 42 : parcours du tableau
  • ligne 43 : $groupe[$i] est un objet de type Personne. La méthode __toString est utilisée pour l'afficher.

5.8. Création d'une classe dérivée de la classe Personne (exemple_21)


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;
  
// getters et setters
  public function getPrénom() {
    return $this->prénom;
  }
...

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // méthode
  public function initWithPersonne($p) {
    // initialise l'objet courant avec une personne $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // méthode toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// une classe dérivée de Personne
class Enseignant extends Personne {

  // attributs
  private $discipline;   // discipline enseignée

  // getter et setter

  public function getDiscipline() {
    return $this->discipline;
  }

  public function setDiscipline($discipline) {
    $this->discipline = $discipline;
  }

  // constructeur

  public function __construct($prénom, $nom, $âge, $discipline) {
    // attributs parent
    parent::__construct($prénom, $nom, $âge);
    // autres attributs
    $this->setDiscipline($discipline);
  }

  // surcharge de la fonction __toString de la classe parente
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// test
// création d'un tableau d'objets Personne et dérivés
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70));

// identité de ces personnes
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// fin
exit;
?>

Résultats :

groupe[0]=[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]

Commentaires

  • lignes 3-35 : la classe Personne
  • ligne 38 : la classe Enseignant dérive (extends) de la classe Personne. La classe dérivée Enseignant hérite des attributs et des méthodes de sa classe mère.
  • ligne 41 : la classe Enseignant ajoute un nouvel attribut discipline qui lui est propre
  • ligne 55 : le constructeur de la classe Enseignant reçoit 4 paramètres
    • 3 pour initialiser sa classe parent (prénom, nom, âge)
    • 1 pour sa propre initialisation (discipline)
  • ligne 57 : la classe dérivée a accès aux méthodes et constructeurs de sa classe parent via le mot clé parent ::. Ici on passe les paramètres (prénom, nom, âge) au constructeur de la classe parent.
  • ligne 64 : la méthode __toString de la classe dérivée utilise la méthode __toString de la classe parent.
  • ligne 71 : un tableau contenant un objet Enseignant et un objet Personne.
  • ligne 74 : on boucle sur les éléments du tableau
  • ligne 75 : la méthode __toString de chaque élément $groupe[$i] va être appelée. La classe Personne a une méthode __toString. La classe Enseignant en a deux : celle de sa classe parent et la sienne propre. On peut se demander laquelle va être appelée. L'exécution montre que c'est celle de la classe Enseignant qui a été appelée. C'est toujours ainsi : lorsqu'une méthode est appelée sur un objet, on cherche celle-ci dans l'ordre suivant : dans l'objet lui-même, dans sa classe parent s'il en a une, puis dans la classe parent de la classe parent, etc … La recherche s'arrête dès que la méthode a été trouvée.

5.9. Création d'une seconde classe dérivée de la classe Personne (exemple_22)

L'exemple suivant crée une classe Etudiant dérivée de la classe Personne :


<?php

class Personne {

// attributs de la classe
  private $prénom;
  private $nom;
  private $âge;

// getters et setters
  public function getPrénom() {
    return $this->prénom;
  }
...

// constructeur
  function __construct($prénom, $nom, $âge) {
    // on passe par les set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // méthode
  public function initWithPersonne($p) {
    // initialise l'objet courant avec une personne $p
    ...
  }

  // méthode toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// une classe dérivée de personne
class Enseignant extends Personne {

  // attributs
  private $discipline;   // discipline enseignée

  // getter et setter
...

  // constructeur

  public function __construct($prénom, $nom, $âge, $discipline) {
    // attributs parent
    parent::__construct($prénom, $nom, $âge);
    // autres attributs
    $this->setDiscipline($discipline);
  }

  // surcharge de la fonction _identité__toString de la classe parente
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// une classe dérivée de Personne
class Etudiant extends Personne {

  // attributs
  private $formation;   // discipline enseignée

  // getter et setter

  public function getFormation() {
    return $this->formation;
  }

  public function setFormation($formation) {
    $this->formation = $formation;
  }

  // constructeur

  public function __construct($prénom, $nom, $âge, $formation) {
    // attributs parent
    parent::__construct($prénom, $nom, $âge);
    // autres attributs
    $this->setFormation($formation);
  }

  // surcharge de la fonction __toString de la classe parente
  public function __toString() {
    return "[" . parent::__toString() . ",$this->formation]";
  }

}

// test
// création d'un tableau d'objets personne et dérivés
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70), new Etudiant("Steve", "Boer", 23, "iup2 qualité"));

// identité de ces personnes
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// fin
exit;
?>

Résultats :

groupe[0]=[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
groupe[2]=[[Steve,Boer,23],iup2 qualité]

5.10. Les interfaces (exemple_23)

Une interface est une structure définissant des prototypes de méthodes. Une classe implémentant une interface définit le code de toutes les méthodes de l'interface.


<?php

// une interface
interface IExemple {
  function ajouter($i, $j);

  function soustraire($i, $j);
}

// implémentation 1 de l'interface
class Classe1 implements IExemple {

  public function ajouter($a, $b) {
    return $a + $b + 10;
  }

  public function soustraire($a, $b) {
    return $a - $b + 20;
  }

}

// implémentation 2 de l'interface
class Classe2 implements IExemple {

  public function ajouter($a, $b) {
    return $a + $b + 100;
  }

  public function soustraire($a, $b) {
    return $a - $b + 200;
  }

}

// fonction
function calculer($a, $b, IExemple $interface) {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}

// ------------------- main
// création de 2 objets de type Classe1 et Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// appel de la fonction calculer
calculer(4, 3, $c1);
calculer(14, 13, $c2);
?>

Résultats

17
21
127
201

Commentaires

  • lignes 4-8 : l'interface IExemple définit deux méthodes : la fonction ajouter (ligne 5) et la fonction soustraire (ligne 7). L'interface ne définit pas le code de ces méthodes. Ce sont les classes implémentant l'interface qui vont le faire.
  • ligne 11 : la classe Classe1 implémente (implements) l'interface IExemple. Elle définit donc un code pour les méthodes ajouter (ligne 13) et soustraire (ligne 17).
  • lignes 24-34 : idem pour la classe Classe2
  • ligne 37 : la méthode calculer admet trois paramètres : deux entiers $a et $b et un objet $interface implémentant l'interface IExemple. On aurait pu écrire function calculer($a,$b,$interface) sans indiquer le type IExemple du paramètre $interface. En l'indiquant, on permet à l'interpréteur PHP de vérifier que le paramètre effectif est bien un objet implémentant l'interface IExemple.
  • lignes 38-39 : utilisation des méthodes ajouter et soustraire de l'objet $interface. Parce que celui-ci implémente l'interface IExemple, on sait qu'il possède ces deux méthodes.
  • lignes 44-45 : création de deux objets de types différents mais implémentant tous deux l'interface IExemple.
  • ligne 47 : on passe à la fonction calculer, l'objet $c1 de type Classe1 implémentant l'interface IExemple.
  • ligne 48 : on passe à la fonction calculer, l'objet $c2 de type Classe2 implémentant l'interface IExemple.