Maintenant que vous savez pourquoi et comment mettre en place l’héritage, nous allons découvrir comment profiter des propriétés et méthodes héritées. :)
Accédez aux propriétés de la classe parente
Lorsque vous utilisez l'héritage, depuis l'objet comme depuis la classe, vous pouvez accéder aux propriétés de la classe parente de la même manière qu'avant, avec la flèche ->
, comme vous pouvez le voir à la ligne 22 :
<?php
declare(strict_types=1);
class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public function __construct(public string $username, public string $status = self::STATUS_ACTIVE)
{
}
}
class Admin extends User
{
// ...
public function printStatus()
{
// vous pouvez accéder au statut comme si la propriété appartenait à Admin :)
echo $this->status;
}
}
$admin = new Admin('Lily');
$admin->printStatus();
Il en va de même pour les propriétés statiques.
Depuis la classe enfant, vous pourrez faire référence aux mêmes propriétés statiques que le parent, peu importe que vous fassiez référence à la classe enfant ou à la classe parente. En revanche, ceci marche en cascade : vous ne pourrez pas faire référence aux propriétés (statiques ou non) de l'enfant depuis le parent. Voyez ceci comme des poupées russes. Les parents sont au centre. Vous pourrez atteindre un parent en passant par un enfant, l’inverse n’est pas vrai.
Pour faire référence à un parent, vous devez utiliser le nouveau mot clé parent. Il permet de faire référence à une classe parente. En revanche, lorsque votre classe hérite d’une classe, qui elle-même hérite d’une classe, qui elle-même… bref, vous ne pouvez pas cibler la classe parente désirée, avec le mot clé parent
vous remontez (PHP remonte tel un saumon) l’arbre généalogique, jusqu'à trouver l’élément parent auquel vous faites référence. S’il s’agit d’une méthode, elle peut alors elle aussi faire référence à un parent. :)
<?php
declare(strict_types=1);
class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public static $nombreUtilisateursInitialisés = 0;
public function __construct(public string $username, public string $status = self::STATUS_ACTIVE)
{
}
}
class Admin extends User
{
public static $nombreAdminInitialisés = 0;
// mise à jour des valeurs des propriétés statiques de la classe courante avec `self`, et de la classe parente avec `parent`
public static function nouvelleInitialisation()
{
self::$nombreAdminInitialisés++; // classe Admin
parent::$nombreUtilisateursInitialisés++; // classe User
}
}
Admin::nouvelleInitialisation();
var_dump(Admin::$nombreAdminInitialisés, Admin::$nombreUtilisateursInitialisés, User::$nombreUtilisateursInitialisés);
var_dump(User::$nombreAdminInitialisés); // ceci ne marche pas.
Accédez aux méthodes de la classe parente
En ce qui concerne les méthodes de classe, depuis un objet, tout comme les accès aux propriétés, rien ne change. Vous pourrez accéder aux méthodes de la même manière qu'avant, avec la flèche ->
comme vous pouvez le voir à la ligne 28 :
<?php
declare(strict_types=1);
class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public function __construct(public string $username, public string $status = self::STATUS_ACTIVE)
{
}
public function printStatus()
{
echo $this->status;
}
}
class Admin extends User
{
// ...
public function printCustomStatus()
{
echo “L’administrateur {$this->username} a pour statut : “;
$this->printStatus(); // on appelle printStatus du parent depuis la classe enfant
}
}
$admin = new Admin('Lily');
$admin->printCustomStatus(); // Affiche “L’administrateur Lily a pour statut : active”
$admin->printStatus(); // printStatus n’existe pas dans la classe Admin, donc printStatus de la classe User sera appelée grâce à l’héritage
PHP vous permet également de réécrire une méthode existante d’un parent, dans une classe enfant. On parle de surcharge.
Que vous permet de faire PHP, et que vous impose-t-il lorsque vous réécrivez/surchargez une méthode existante dans une classe parente ? Pour commencer, sa signature doit rester compatible avec la méthode d'origine :
vous ne pouvez pas enlever des arguments ;
vous pouvez ajouter un argument uniquement s'il est optionnel ;
Vous pouvez changer le type d'un argument uniquement s'il est compatible avec le type d'origine (voir un exemple) ;
vous pouvez changer le type de retour de la méthode uniquement s'il est compatible avec le type d'origine.
Lorsque vous surchargez une méthode, vous pouvez choisir d’exploiter la méthode parente et d’ajouter un comportement supplémentaire, ou de choisir de complètement réécrire le comportement. Dans le premier cas, lorsque vous souhaitez appeler la méthode parente, plutôt que la flèche, il faut utiliser parent::
.
<?php
declare(strict_types=1);
class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public function __construct(public string $username, public string $status = self::STATUS_ACTIVE)
{
}
public function setStatus(string $status): void
{
if (!in_array($status, [self::STATUS_ACTIVE, self::STATUS_INACTIVE])) {
trigger_error(sprintf('Le status %s n\'est pas valide. Les status possibles sont : %s', $status, implode(', ', [self::STATUS_ACTIVE, self::STATUS_INACTIVE])), E_USER_ERROR);
};
$this->status = $status;
}
public function getStatus(): string
{
return $this->status;
}
}
class Admin extends User
{
public const STATUS_LOCKED = 'locked';
// la méthode est entièrement réécrite ici :) seule la signature reste inchangée
public function setStatus(string $status): void
{
if (!in_array($status, [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_LOCKED])) {
trigger_error(sprintf('Le status %s n\'est pas valide. Les status possibles sont : %s', $status, implode(', ', [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_LOCKED])), E_USER_ERROR);
};
$this->status = $status;
}
// la méthode utilisée est celle de la classe parente, puis ajoute un comportement :)
public function getStatus(): string
{
return strtoupper(parent::getStatus());
}
}
$admin = new Admin('Paddington');
$admin->setStatus(Admin::STATUS_LOCKED);
echo $admin->getStatus();
Comme vous avez pu le voir dans l’exemple précédent ciblant une propriété parente, lorsqu’une méthode est héritée depuis une classe enfant, vous pouvez choisir :
d’appeler la méthode de la classe courante avec
$this
(ouself
pour les méthodes statiques) ;ou bien de cibler la méthode parente avec le mot clé
parent::
.
Exercez-vous
Dans cet exercice, le code de la salle d'attente (la classe Lobby
) existe, ainsi que le code d'un joueur (la classe Player
). Lorsqu'un joueur s'enregistre dans le Lobby, il devient un Joueur en Attente.
Un joueur en attente possède une propriété range
qui est un entier. Le but de cette propriété est d'accroître la portée de la recherche d'un adversaire, lorsqu'aucun ne correspond au niveau du joueur. Le but étant de trouver un adversaire, quitte à ce qu'il soit plus faible ou plus fort.
Votre tâche est de :
créer une classe
QueuingPlayer
qui étend la classePlayer
;et de lui ajouter la propriété
range
.
Vous trouverez le code de l’exercice sur la branche P2C2, et la correction sur la branche P2C2-correction. :)
En résumé
La façon d’accéder aux propriétés des classes parentes s’effectue avec la flèche
->
.La façon d’accéder aux méthodes des classes parentes s’effectue également à l’aide de la flèche
->
.Lorsqu’il s’agit d’une méthode ou d’une propriété statique parente, l’accès s’effectue avec le mot clé
parent
.Vous pouvez ré-écrire une méthode existante dans un enfant, à condition de respecter sa signature.
Réduire notre code grâce à l’héritage est une bonne chose, mais avec le temps vous vous apercevrez que les visibilités public ou private ne sont plus suffisantes. Regardons comment limiter l’accès aux propriétés et aux méthodes avec l’héritage, dans le chapitre suivant. C’est parti !