Apprendre la POO en PHP #1 – Classes, propriétés et méthodes

Découverte de ce paradigme de programmation et premiers éléments de syntaxe en PHP.

Tu as fait le tour de nos contenus « Apprendre le PHP », tu les as mis en pratique dans des challenges ou dans un de tes projets et tu souhaites aller + loin ?

Bienvenue dans le monde merveilleux de la Programmation Orientée Objet (POO) !

Au programme de ce premier tutoriel :

Qu’est ce que la Programmation Orientée Objet (POO) ?

La Programmation Orientée Objet est un paradigme de programmation qui organise le code avec des objets. Ces objets vont représenter des entités ou concepts les plus réels possibles. C’est à dire qu’on va faire en sorte que le code représente des éléments palpables du contexte d’un projet.

S’il est question par exemple d’un programme pour une boutique ecommerce, alors on sera amené à créer ces objets :

  • Un objet Produit
  • Un objet Panier
  • Un objet Commande
  • Un objet Paiement
  • etc.

Chaque objet a ensuite ses propres attributs, appelés « propriétés » et ses propres comportements, appelés « méthodes ». Pour rester dans l’exemple précédent :

  • Un objet Produit peut avoir une propriété « nom », une propriété « description », une propriété « visuel », etc.
  • Un objet Paiement peut avoir comme comportement « l’exécution du paiement », ou encore la gestion d’une erreur de paiement, etc.

Chaque objet est l’instance d’une classe, qui définit la structure et les communes à plusieurs objets similaires. Disons qu’on travaille sur un système ecommerce de vente de produits alimentaires :

  • J’ai un objet « pâtes » qui est une instance de la classe « Produit »
  • J’ai un objet « sauce » qui est une instance de la classe « Produit »
  • J’ai un objet « fromage » qui est une instance de la classe « Produit »
  • J’ai donc plusieurs objets qui sont des instances de la même (et unique) classe Produit

Importance de la POO

La Programmation Orientée Objet donne un cadre et des conventions pour écrire et organiser son code, ce qui en favorise la réutilisation, la modularité et la maintenance.

La grande majorité des programmes évolués, des frameworks ou librairies sont codées en POO. Il est donc primordial de maitriser ce paradigme de programmation.

On va commencer tranquillement par créer une première classe 🙂

Les classes en PHP

Pour commencer, il faut lui trouver un nom. On respectera le format « PascalCase », c’est à dire qu’on commence par une majuscule puis en minuscules, puis de nouveau une majuscule à chaque changement de mot.

Pour notre exemple, on va créer un objet « Student », un élève (on va coder en anglais pour respecter les conventions). Voici comment on va créer notre classe :

// Dans un fichier dédié Student.php
class Student
{
   // Contenu de la classe
}

Un peu d’explications :

  • (Très) souvent, on mettra les classes dans des fichiers dédiés, dont le nom du fichier correspond exactement au nom de la classe
  • On utilise le mot clé class
  • Puis le nom de la classe en PascalCase
  • On passe à la ligne et on ouvre les accolades

Une classe, c’est un peu comme une fonction, tant qu’on ne l’utilise pas, elle ne sert pas à grand chose ! On parle d’appel à une fonction. Et ici on va parler d’instanciation d’une classe :

// Dans mon programme principal
$student = new Student;

Un peu d’explications :

  • On utilise le mot clé new pour créer une nouvelle instance de la classe
  • $student va contenir une référence à l’instance de la classe Student (on reviendra sur cette notion de référence un peu + tard).

Les propriétés en PHP

Les propriétés peuvent être considérées comme des variables de la classe, qui servent à définir ses données internes, son état, etc.

Pour notre élève, on peut avoir ces informations :

  • Données internes : prenom, nom, age
  • Etat : a-t-il suivi la formation d’accueil ?

Voici comment coder ces propriétés :

class Student
{
   public string $firstName;
   public string $lastName;
   public int $age;

   public bool $hasFollowOnboarding = false;
}

Un peu d’explications :

  • Chaque propriété est définie sur une ligne dédiée
  • On commence avec le mot clé public qui représente la visibilité de la propriété
  • On précise le type de la propriété
  • On indique le nom de la propriété, comme on créerait une variable, avec un $
  • Pour le booléen $hasFollowOnboarding, j’ai indiqué une valeur par défaut

La visibilité

Il existe 3 + 1 visibilité différentes :

  • public : cela signifie que les propriétés sont accessibles depuis l’extérieur de l’objet, c’est à dire que le programme principal pourrait les lire et modifier.
  • private : cela signifie que les propriétés ne sont accessibles qu’à l’intérieur de l’objet, c’est à dire que le programme principal ne peut ni les lire, ni les modifier.
  • protected : cela signifie que les propriétés ne sont accessibles qu’à l’intérieur de l’objet ET avec ses classes parentes ou enfants. On verra ses notions dans le contexte de l’héritage. Les propriétés protected ne sont pas accessibles dans le programme principal.

Il y a en complément la notion de « readonly » qui signifie « lecture seule » qui permet de préciser qu’une propriété ne pourra être définie qu’une seule fois. On pourra l’initialiser, mais on ne pourra plus modifier sa valeur par la suite.

Retravaillons le code précédent avec ces éléments :

class Student
{
   public readonly string $firstName;
   public readonly string $lastName;
   public readonly int $age;

   private bool $hasFollowOnboarding = false;
}

Avant de manipuler ces propriétés depuis le programme principal, on va définir ce que sont les méthodes.

Les méthodes en PHP

Les méthodes peuvent être considérées comme des fonctions de la classe, qui peuvent effectuer des opérations sur les propriétés pour renvoyer, ou non, des données vers le programme principal.

Le parallèle avec les fonctions s’applique notamment car les méthodes vont avoir aussi :

  • Des paramètres (il peut aussi ne pas y en avoir)
  • Un type de retour (on peut préciser qu’on ne retournera rien, mais cela est un type en soi)

Le constructeur et $this

Le constructeur est la méthode magique (oui c’est le vrai terme) qui sera appelée de façon automatique lorsqu’on instancie une classe.

Le constructeur peut avoir des paramètres. On va faire en sorte ici de définir le prenom et nom de l’élève :

class Student
{
   public readonly string $firstName;
   public readonly string $lastName;
   public int $age;

   private bool $hasFollowOnboarding = false;

   // Le constructeur NE DOIT PAS avoir de type de retour
   public function __construct(string $firstName, string $lastName)
   {
      $this->firstName = $firstName;
      $this->lastName = $lastName;
   }
}

Un peu d’explications :

  • La notion de visibilité présentée pour les propriétés s’applique également pour les méthodes (sauf qu’il n’y a pas de readonly pour les méthodes)
  • On écrit le mot clé function
  • Le constructeur s’écrit __construct (double underscore) c’est comme ça. Sinon on respecte les conventions de nommage des fonctions pour les méthodes.
  • Ici notre constructeur a 2 paramètres, 2 chaines de caractères nommées $firstName et $lastName. Attention : il s’agit bien là de paramètres de la méthode. Il n’y a pas de lien technique ou autre entre le paramètre $firstName et la propriété $firstName
  • On fait appel à la propriété grâce au mot clé $this-> qui permet de faire référence à l’objet lui même. Puis le nom de la propriété, sans le $. Une erreur commune au début est d’écrire $this->$firstName mais cela va engendrer un comportement inattendu, voir une erreur.

Pour activer le constructeur, voici comment faire lors de l’instanciation :

// On appelle la classe "à la manière" de l'appel d'une fonction
$student = new Student('John', 'Doe');

Cette façon d’écrire un constructeur est intéressante pour aborder les méthodes et $this mais est obsolète depuis PHP 8. On peut désormais utiliser la promotion de propriétés dans le constructeur, ce qui donnerait :

class Student
{
   // Propriétés hors constructeur
   public readonly int $age;
   private bool $hasFollowOnboarding;

   // Constructeur avec promotion de propriétés
   public function __construct(
      public readonly string $firstName,
      public readonly string $lastName,
   ) {}
}

Usages des propriétés

Voyons voir comment je peux utiliser les propriétés dans le programme principal :

// Dans mon programme principal
$student = new Student('John', 'Doe');

// On utilise la flèche pour faire référence à une propriété
// Comme ces propriétés sont publiques je peux les appeler
echo 'L\'élève s\'appelle ' . $student->firstName . ' ' . $student->lastName;

// Cette ligne renvoie une ERREUR car cette propriété n'a pas encore de valeur
// Aucune valeur par défaut et aucune initialisation
echo 'Son âge est ' . $student->age . ' ans.';

// Il faut donc l'initialiser avant de cette façon : (attention, ceci n'est pas une bonne pratique, on va le voir juste après)
$student->age = 30;

// Cette ligne renvoie une ERREUR car cette propriété, bien que définie avec une valeur par défaut, est privée (private)
if ($student->hasFollowOnboarding) {
   // ...
}

Nous allons créer d’autres méthodes pour manipuler les propriétés.

Autres méthodes, types de retour, getters et setters

Une première méthode pour initialiser l’âge puisque celui-ci n’est pas géré par le constructeur :

// Dans l'objet
public function setAge(int $age): void
{
   $this->age = $age;
}

Un peu d’explications :

  • On a une mécanique similaire à celle du premier constructeur : le paramètre de la méthode nous sert à initialiser la valeur de la propriété
  • La méthode retourne void c’est à dire qu’elle ne retourne rien ! Mais on le précise quand même, cela fait partie de la déclaration de la méthode. De cette façon si en faisant évoluer le code on rajoutait un return, on aurait une erreur nous indiquant que la déclaration n’est plus respectée.
  • Ce type de méthode s’appelle un « setter ». C’est une méthode qui sert seulement à définir la valeur d’une propriété. Ca a beaucoup de sens lorsque les propriétés sont privées (private). Il est intéressant de mettre par défaut toutes les propriétés en privées, puis « d’alléger » leur visibilité en fonction des besoins du code.

Le pendant du setter est le getter, qui sert justement à accéder à une propriété privée en dehors de l’objet :

// Dans l'objet -----------------
private int $age;

// Setter
public function setAge(int $age): void
{
   $this->age = $age;
}

// Getter
public function getAge(): int
{
   return $this->age;
}

// Dans le programme principal -------------
$student = new Student('John', 'Doe');

$student->setAge(30);

// Cette fois-ci aucune erreur !
echo 'Son âge est ' . $student->getAge() . ' ans.';

Un peu d’explications :

  • Les 2 méthodes sont en public comme on a besoin de pouvoir les utiliser dans le programme principal. Là où les propriétés seront bien souvent privées (ou public readonly), les méthodes sont bien souvent publiques.
  • La méthode getAge() n’a pas de paramètre. Elle n’en a pas besoin comme elle va faire appel à une propriété de l’objet, qu’elle va d’ailleurs retourner. C’est pour ça que son type de retour correspond au type de la propriété.
  • Dans le programme principal, si une propriété n’a pas de valeur par défaut, il est important de faire d’abord le setter avant le getter.

Créons d’autres méthodes :

// L'étudiant.e suit le parcours de formation
public function followOnboarding(): void
{
   $this->hasFollowOnboarding = true;
}

// L'étudiant fête son anniversaire
public function birthday(): void
{
   $this->age++;
}

// Les informations de l'étudiant au format HTML
public function getCompleteInformationsInHtml(): string
{
   $out = '<ul>';

   $out .= '<li>Prénom : ' . $this->firstName . '</li>';
   $out .= '<li>Nom : ' . $this->lastName . '</li>';
   $out .= '<li>Âge : ' . $this->getAge()  . '</li>'; // Utilisation d'une méthode interne

   if ($this->hasFollowOnboarding) {
      $out .= '<li>Onboarding bien suivi !</li>'; 
   }

   return $out;
}

Un peu d’explications :

  • La première méthode followOnboarding s’apparente à un setter sauf qu’on ne peut pas vraiment gérer la valeur, on peut juste la passer à true. Quand décrit dans l’introduction, la POO permet de représenter le monde réel. Une fois qu’on a suivi l’onboarding, on ne peut plus le « dé-suivre ».
  • La deuxième méthode birthday fait évoluer l’âge. La valeur de la propriété va changer (incrément de 1). Pour autant, la méthode ne retourne rien.
  • La troisième méthode getCompleteInformationsInHtml renvoie une chaine de caractères. On construit cette chaine au fil de la méthode, en créant une variable, en concaténant avec les propriétés, en réalisant une condition, puis on retourne la valeur. Comme une fonction, une méthode peut contenir tout un ensemble d’instructions.
  • Remarque : L’utilisation du getter getAge() est superflue comme on se trouve dans la classe, on peut appeler $this->age directement. Mais je voulais te montrer qu’une méthode peut faire appel à une autre méthode !

Pour aller + loin sur les notions de typage, tu peux retrouver la liste des types disponibles en PHP.

Et n’hésite pas à te référer à un corrigé en PHP pour voir d’autres exemples + poussés de méthodes.

La notion de référence en POO en PHP

Définissons une petite classe assez simple qui va nous permettre d’illustrer la notion de référence en POO :

class School
{
   public string $name;
}

On oublie les bonnes pratiques énoncées au dessus. On va créer un objet et le modifier :

// Je crée une école et je lui donne un nom
$ecoleA = new School;
$ecoleA->name = 'Harvard';

// Je copie ecoleA dans ecoleB
$ecoleB = $ecoleA;

// Je change ecoleB
$ecoleB->name = 'Oxford';

// Et maintenant...
echo $ecoleA->name; // ??
echo $ecoleB->name; // ??

Instinctivement, on se dit que $ecoleA->name vaut toujours « Harvard » comme on l’a changé sur $ecoleB et non sur $ecoleA… Et bien non, les 2 echo vont afficher la même chose, à savoir « Oxford ».

En fait, $ecoleA ne contient pas l’objet, il contient la référence à celui-ci. Quand on fait le new, on crée un objet (= une instance de classe), et c’est la référence à l’objet qui est stockée dans la variable. Comme si la variable stockait la direction vers l’objet, l’endroit où il est rangé.

Donc lorsque je fais :

$ecoleB = $ecoleA;

Et bien, je ne copie pas l’objet, je copie la référence, donc les 2 variables pointent vers le même objet.

Pour copier un objet il faut utiliser la méthode clone. Mais je ne la présente pas tout de suite car on n’a pas forcément besoin de l’utiliser si souvent que ça.

Quelques informations complémentaires et exemples sur les références sur la modification des valeurs d’un tableau avec foreach en PHP.

Conlusion

Voilà pour un premier tour d’horizon de la POO en PHP !

Pour mettre en pratique et assimiler ces concepts, je t’invite à choisir un challenge de code débutant et à le résoudre avec une logique POO.


Qui a codé ce superbe contenu ?

Keep learning

Other content to discover