Réponse AJAX
Objectif
- L'objectif de cet atelier est de découvrir les syntaxes des objets en PHP.
- Cela suppose que vous connaissez déjà les principes de bases de la programmation objet : classes, instanciation, constructeurs, accesseurs, héritage.
- Vous découvrirez également comment écrire et lire facilement des objets dans une base de données MySQL, grâce à la sérialization.
Créer une classe et l'instancier
La classe
La syntaxe pour créer une classe est la suivante :
Les propriétés
Ici on rajoute les propriétés nom, prénom et date de naissance :
- On peut déclarer une propriété avec les mots-clés suivants :
- public : tout le monde a le droit d'accéder directement à la propriété, y compris les utilisateurs de la classe.
- protected : seules les classes parentes et les classes héritées ont accès aux propriétés déclarées comme protected. Les utilisateurs de la classe n'y ont pas accès.
- private : seule la classe peut, en interne, accéder aux propriétés déclarées comme private. Les utilisateurs, les classes parentes et les classes héritées n'y ont pas accès.
Les méthodes
Ici on rajoute une méthode getAge, qui renvoie l'âge en fonction de la date de naissance (en supposant que la date de naissance est au format "1973-02-25").
Remarquez $this qui représente l'objet courant, et $this->dateDeNaissance qui permet d'accéder à une de ses propriétés :
Instanciation de la classe, mot-clé new
Voici le code qui permet d'instancier la classe, de définir le nom et la date de naissance, puis d'afficher l'âge.
Remarquez le mot-clé new qui permet d'instancier :
Ajoutez une méthode getPatronyme() qui renvoie "Prénom NOM" :
Votre réponse n°1
Modifiez la méthode getAge() de façon à ce qu'elle renvoie "âge inconnu" lorsque la date de naissance a pour valeur null (utilisez is_null),
et elle renvoie par exemple "32 ans" lorsque la date de naissance est connue.
Gérez le "s" du pluriel : "1 an", "2 ans", ...
Votre réponse n°2
Constructeurs et destructeurs
Constructeur PHP (depuis PHP 5)
Voici un constructeur PHP (function __construct()
). On écrit donc :
Constructeur paramétré
Et voici un script avec un constructeur qui reçoit cette fois-ci des paramètres (optionnels puisqu'on leur a donné une valeur par défaut grâce à =null
).
Remarquez les différentes instanciations, toutes valables :
Destructeur PHP 5
Voici la syntaxe du destructeur en PHP :
Le Destructeur est appelé au moment où on détruit l'objet, c'est à dire :
- lorsque le script PHP se termine (PHP libère toutes les ressources lorsque le script se termine),
- lorsque vous détruisez explicitement l'objet avec la fonction unset :
Le destructeur permettra de libérer les ressources utilisées par l'objet (connexions aux BDD, fermeture de fichiers, etc.).
Les accesseurs, le mot-clé protected
L'accesseur permet de lire et modifier une propriété indirectement grâce à une méthode dédiée (au lieu d'accéder directement à la propriété).
Voici par exemple les accesseurs pour le nom de la classe personne
. Remarquez le mot-clé protected
qui interdit d'accéder directement à la propriété (il existe également private
qui est encore plus restrictif,plus d'infos ici) :
Ou voilà une autre écriture plus élégante (dans la logique de JQuery), où le même accesseur joue le rôle de lecture et d'écriture :
Quel message d'erreur obtenez-vous si, en dehors de la classe, vous essayez d'accéder à une propriété déclarée comme protected ?
Votre réponse n°3
Écrivez pour les accesseurs le prénom et la date de naissance.
Votre réponse n°4
Les méthodes de classe, le mot-clé static
- Une méthode de classe est liée à la classe et non pas à un objet de cette classe. On n'a donc pas besoin d'instancier un objet de cette classe pour pouvoir l'appeler.
- Par exemple, voici une méthode de classe qui affiche la version de la classe et le copyright.
Une méthode de classe n'a rien de particulier dans sa déclaration, si ce n'est, bien évidemment, qu'elle n'a absolument pas le droit d'utiliser $this (puisqu'elle ne s'applique pas à un objet). :
- Et voici un appel à cette méthode de classe, remarquez la syntaxe particulière (la méthode s'applique à la classe et non pas à une instance de la classe) :
Ecrire et lire un objet simple dans une base de données MySQL
- Dans vos classes, vous aurez très souvent besoin de :
- une méthode
toMysql
, pour écrire un objet dans une base de données,
- une méthode de classe
fromMysql
, pour récupérer un objet depuis la base (selon sa clé primaire par exemple).
Introduction à ma méthode
- Un objet est quelque chose de compliqué : il peut être de sous-classes différentes (dans le cas où on fait l'héritage), il peut donc posséder un nombre de champs variable.
-
Pour gérer ces différences, on dispose de plusieurs solutions compliquées dans la base de données :
- Prévoir tous les champs possibles pour la classe et ses sous-classes réunies, et mettre la valeur null pour ceux qui n'existent pas. Problèmes posés par cette solution :
- on ne sait pas si un champ avec la valeur null signifie "la propriété n'existe pas pour cet objet" ou "la propriété a pour valeur null".
- on peut avoir beaucoup de champs inutilisés.
- Prévoir une table différente pour la classe et pour chaque classe héritée. Problème posé par cette solution : les requêtes deviennent extrêmement complexes.
- La solution la plus simple sera de sérialiser l'objet (grâce à serialize) pour le transformer en une chaîne de caractères.
On le stocke alors dans un champ de type text (évitez le varchar, souvent trop court). Problèmes posés par cette solution :
- un objet sérialisé prend plus de taille mémoire que l'original, on n'optimise donc pas la taille de la base de données.
- les propriétés de l'objet, une fois sérialisées, ne peuvent pas faire l'objet d'une requête SQL.
- Lorsqu'on relira l'objet, on va le désérialiser (grâce à unserialize) et récupérer ainsi l'objet d'origine.
- On n'oubliera pas de séparer dans des champs distincts chaque propriété susceptible de faire l'objet d'une requête SQL (pour y accéder facilement sans désérialiser l'objet). Sont concernés : la clé primaire, les clés étrangères (pour les jointures), les champs susceptibles de faire l'objet d'une recherche.
Ecriture
- Ce paragraphe suppose que c'est MySQL qui, grâce à l'auto-incrément, choisit la valeur de la clé primaire pour les nouveaux objets. D'autres solutions sont envisageables et nécessites une adaptation du code (très minime toutefois).
-
Créez la table suivante :

- Voici la méthode qui permet d'écrire un objet de classe personne
(ou de n'importe quelle autre classe, d'ailleurs), dans cette table :
- Vous remarquez que :
- Ce script ne s'occupe pas de la connexion à la base de données, qui est supposée déjà faite.
- Si l'id de l'objet est null, on considère qu'il vient d'être créé par le code PHP et qu'il n'existe pas encore dans la base de données. On exécute donc une requête INSERT. Sinon on exécute une requête UPDATE. Vos programmes pourront avoir un comportement différent : on pourrait très bien avoir une valeur pour l'id sans que l'objet existe dans la base. Il faut alors un champ supplémentaire pour gérer l'existence ou non de l'objet dans la base.
- Etant donné que c'est MySQL qui choisit l'id (grâce à l'auto_increment), je synchronise l'id choisi par MySQL et l'id de mon objet grâce à la ligne 10 (mysqli_myinsert_id). Inutile si on ne se sert plus de l'objet après l'avoir écrit dans la base.
- Si vous analysez bien le contenu de la base de données, vous voyez une désynchronisation entre l'id dans le champ "id" et l'id de l'objet dans le champ "objet". C'est logique : au moment où l'on a inséré l'objet, on ne connaissait pas encore la valeur de l'auto-incrément qui serai générée pour cet objet. Si on veut régler ce problème, il faut faire une double-écriture (INSERT pour générer l'auto-incrément, puis UPDATE pour écrire l'objet dans la base). Ici, je ne vais réparer ce problème qu'au moment de la lecture, les valeurs resteront désynchronisées dans la base.
Lecture
- Voici la méthode de classe qui effectue la relecture. Il corrige le problème de la désynchronisation d'id évoqué à l'étape précédente (grâce à la ligne 6).
- C'est une méthode de classe puisque l'objet n'existe pas avant qu'on le crée... et cette méthode sert à le créer ! On l'appelle donc ainsi :
- Ceci marchera, que l'objet soit de la classe
personne
ou de n'importe quelle classe fille.
Dans votre base de données v_anonyme_db, créez une table pour accueillir les personnes ci-dessus.
À partir de la classe Personne ci-dessus, écrivez vos méthodes :
from_mysql(id) qui récupère l'objet depuis la base de données,
to_mysql() qui écrit ou modifie l'objet dans la base de données.
Remarque : ajoutez une propriété 'id'. Quand on crée une nouvelle personne, son id est null. C'est quand on l'écrit pour la première fois dans la base de données que MySQL choisit un ID automatique, qu'on affecte aussitôt à notre objet (de façon à garder synchronisées les données dans MySQL et en PHP).
Collez l'ensemble du code ici après l'avoir testé :
Votre réponse n°5
Héritage et redéfinition de méthodes
Principes
- Le code suivant complète celui tapé précédemment. Il consiste à créer une nouvelle classe
femme_mariee
, qui hérite de la classe personne
. C'est conceptuellement correct car une "femme mariée" est une "personne".
- La femme mariée a (ou avait, parce que les lois ont changé depuis) pour particularité un nom de jeune fille. On va redéfinir (c'est à dire surcharger) la méthode
getPatronyme()
pour l'adapter à ce nouveau cas (pour une femme mariée, on va afficher "Prénom NOM-D-EPOUSE née NOM-DE-JEUNE-FILLE").
- Remarquez que l'héritage multiple n'existe pas de base en PHP. Toutefois on peut utiliser les interfaces dans la plupart des cas pour pallier à ça (différentes classes ont parfois le même comportement, on peut leur associer une interface commune).
- Explications :
- Ligne 4 : remarquez le mot-clé
extends
qui réalise l'héritage.
- Ligne 6 : on crée un nouvelle propriété : la classe est maintenant spécialisée.
- Ligne 15 : on redéfinie la méthode getPatronyme.
- Ligne 17 : remarquez la référence (non-obligatoire) à la méthode de la classe parente grâce à
parent::getPatronyme()
. Quand on re-définie une méthode, on n'est pas obligé de tout ré-écrire car on peut appeler la méthode d'origine.
- Techniques pour simuler l'héritage multiple en PHP 5 : http://jf-lepine.developpez.com/tutoriels/php/heritage-multiple-php/
- Plus d'explications sur la surcharge ici.
Appel aux méthodes de la classe mère
Quand on surcharge les méthodes de la classe mère, on peut faire appel au code d'origine de la classe mère avec :
Les constantes de classe
On peut déclarer des constantes propres à la classe, en utilisant la syntaxe suivante (à partir de PHP 7.1) :
Il existe également le mot-clé parent
(au lieu de self
) qui permet d'accéder aux constantes de la classe mère.
Les classes abstraites
(exemple avec Testomatic)
Une classe abstraite est une classe où certaines méthodes ont seulement leur en-tête de définie, mais elles ne sont pas implémentées (elles n'ont pas de code).
Une telle classe ne peut être instanciée, mais on pourra instancier les classes filles dans lesquelles toutes les méthodes abstraites sont implémentées.
D'autres fonctions et mots-clés liés aux objets
- var_dump($object) : affiche en détail un objet et toutes ses propriétés. Marche aussi avec les tableaux.
- get_class : retourne la classe d'un objet.
- is_a : Vérifie si l'objet fait partie d'une classe ou a cette classe comme parent.
- is_subclass_of : Détermine si un objet est une sous-classe d'une classe donnée en paramètre.
- method_exists, property_exists : vérifie que la méthode / la propriété existe bien pour l'objet donné en paramètre.
Etc., toutes les fonctions ici : http://fr.php.net/manual/fr/ref.classobj.php
Exercice d'application : création d'une classe Collection
On veut écrire une classe Collection.
Elle dispose d'une propriété : un tableau $tab
qui va stocker chaque valeur de la collection (remarque : en environnement PHP et en utilisant un tableau, on n'a pas besoin d'initialiser ce dernier. Il n'en sera pas de même dans la plupart des autres environnements).
Son interface est composée des méthodes publiques suivantes :
- ajoute : ajoute une valeur à la fin de la collection.
- lit_premier : renvoie la première valeur de la collection, place l'index de tableau au début.
- lit_suivant : renvoie la valeur qui suit la dernière valeur lue, place l'index de tableau sur la valeur lue.
- est_dernier : renvoie vrai si on a dépassé (avec lit_suivant) la dernière valeur de la collection.
- retire_premier : renvoie et supprime la première valeur de la collection.
- retire_dernier : renvoie et supprime la dernière valeur de la collection.
- est_vide : renvoie vrai si la collection ne comporte aucune valeur.
- count : renvoie le nombre de valeur dans la collection.
Pour réaliser cela, vous utiliserez les fonctions PHP de traitement de tableau : count, array_pop, array_shift, reset, next, key (renvoie l'indice courant, renvoie null si on a dépassé la fin du tableau).
Est-ce que la propriété $tab
va être déclarée en public
, protected
ou private
? Pourquoi ?
Votre réponse n°6
Ecrivez le code entier de la classe.
Votre réponse n°7
Testez votre classe avec le code suivant. Le résultat affiché doit être : 12765
.
Utilisation de la classe collection
On va rajouter à une personne la liste de ses enfants.
Dans la classe personne, rajoutez une collection $listeEnfants.
Ajouter une méthode ajouteEnfant($prenom, $dateDeNaissance) qui :
- instancie un objet de classe personne qui correspond à l'enfant,
- ajoute cet objet (l'enfant) à la collection $listeEnfants.
Écrivez ici le code que vous avez ajouté à la classe question :
Votre réponse n°8
Dans cette même classe personne, ajouter une méthode afficheFiliation() qui renvoie (return
) les prénoms et âges de tous les enfants d'une personne. Le résultat doit être de la forme :
Bobby (1 an), Marie (4 ans), Paul (7 ans).
Écrivez ici le code de la méthode afficheFiliation() :
Votre réponse n°9
Testez tout ça avec la personne Ella Dégoss (née le 12 avril 1983, mariée, nom de jeune fille : Padgoss), qui a pour enfant Bobby (né le 26 août 2021), Marie (née le 21 juillet 2018) et Paul (né le 15 décembre 2015).
Écrivez ici le code qui sert à tester (n'écrivez pas la classe personne) :
Votre réponse n°10
Faite afficher le résultat des méthodes getPatronyme() et afficheFiliation() puis écrivez ici le résultat obtenu :
Votre réponse n°11