Vous êtes à présent capable de réduire la quantité de code grâce à l'héritage, de toute la finesse nécessaire pour contrôler les différents accès de vos classes grâce à la visibilité, et d'imposer l'écriture d'une méthode ou un héritage avec l'abstraction. Mais il existe encore une situation très commune. L’usage des valeurs des constantes et des propriétés statiques.
En règle générale, ce n’est pas à la classe parente de connaître le fonctionnement des classes enfants. C’est à l’enfant de s’adapter. :) Mais avec les constantes et les propriétés statiques, le code peut anticiper les variations de 2 manières différentes. Voyons ça ensemble !
Changez la valeur d'une propriété statique ou d'une constante
Lorsqu'il s'agit d'une constante, le but est que la valeur ne change pas : il n'est pas prévu qu'une constante soit écrasée. Cependant, il est possible de définir une nouvelle constante portant le même nom dans une classe enfant.
L’exemple suivant n’a pas vraiment de sens pour notre MatchMaker, mais c’est pour illustrer en conservant les éléments dont nous disposons déjà. ;)
<?php
declare(strict_types=1);
abstract class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public function __construct(public string $email, public string $status = self::STATUS_ACTIVE)
{
}
public function setStatus(string $status): void
{
assert(
in_array($status, [self::STATUS_ACTIVE, self::STATUS_INACTIVE]),
sprintf('Le status %s n\'est pas valide. Les status possibles sont : %s', $status, implode(', ',[self::STATUS_ACTIVE, self::STATUS_INACTIVE]))
);
$this->status = $status;
}
public function getStatus(): string
{
return $this->status;
}
abstract public function getUsername(): string;
}
final class Admin extends User
{
public const STATUS_ACTIVE = 'is_active';
public const STATUS_INACTIVE = 'is_inactive';
// Ajout d'un tableau de roles pour affiner les droits des administrateurs :)
public function __construct(string $email, string $status = self::STATUS_ACTIVE, public array $roles = [])
{
parent::__construct($email, $status);
}
public function getUsername(): string
{
return $this->email;
}
// ...
}
$admin = new Admin('michel@petrucciani.com');
var_dump($admin);
$admin->setStatus(Admin::STATUS_INACTIVE);
Si vous exécutez ce code, que constatez-vous ?
La valeur du statut de l’administrateur est maintenant
is_active
au lieu deactive
. Le constructeur de la classeAdmin
fait appel à la constante se trouvant dans celle-ci, à l’aide du mot cléself
.L’assignation du nouveau statut
Admin::STATUS_INACTIVE
échoue, parce que la méthodesetStatus
fait référence aux constantes se trouvant dans la classeUser
. Les valeurs ne correspondent pas !
Comme ici, il arrive de vouloir utiliser une constante de la classe par défaut et d’utiliser une surcharge des classes enfant s’il y en a. Nous souhaitons utiliser les constantes de User
par défaut, mais si une classe enfant les redéfinit (comme c’est le cas avec Admin
), nous souhaitons les utiliser à la place. Pour autoriser cette situation, il faut utiliser un mot clé que vous avez déjà rencontré. Il s'agit de static
. Au lieu de le placer devant une déclaration de méthode ou de propriété, nous allons l'utiliser en remplacement du mot clé self
.
Essayons :
<?php
declare(strict_types=1);
abstract class User
{
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public function __construct(public string $email, public string $status = self::STATUS_ACTIVE)
{
}
// le mot clé self a été modifié pour le mot clé static dans la méthode :)
public function setStatus(string $status): void
{
assert(
in_array($status, [static::STATUS_ACTIVE, static::STATUS_INACTIVE]),
sprintf('Le status %s n\'est pas valide. Les status possibles sont : %s', $status, implode(', ',[static::STATUS_ACTIVE, static::STATUS_INACTIVE]))
);
$this->status = $status;
}
public function getStatus(): string
{
return $this->status;
}
abstract public function getUsername(): string;
}
final class Admin extends User
{
public const STATUS_ACTIVE = 'is_active';
public const STATUS_INACTIVE = 'is_inactive';
// Ajout d'un tableau de roles pour affiner les droits des administrateurs :)
public function __construct(string $email, string $status = self::STATUS_ACTIVE, public array $roles = [])
{
parent::__construct($email, $status);
}
public function getUsername(): string
{
return $this->email;
}
// ...
}
$admin = new Admin('michel@petrucciani.com');
var_dump($admin);
$admin->setStatus(Admin::STATUS_INACTIVE);
Le mot clé static
nous permet d’utiliser la déclaration de la constante la plus proche de la classe instanciée, dans la hiérarchie.
Cette mécanique est appelée late static binding ; en français, résolution statique à la volée. La résolution des valeurs s'effectue non pas lorsque le code est analysé, mais quand il est exécuté.
Pour vous exercer, amusez-vous à ajouter une nouvelle constante DelayConnexion
dans la classe User
, puis dans la classe Admin
.
Exercez-vous
Admettons qu’une compétition spéciale soit organisée. C'est un serveur en mode rapide, et il va rester en ligne pendant 1 semaine. Les joueurs doivent commencer à 1 200 au lieu de 400, et leur ratio doit évoluer 4 x plus vite.
Créez une classe BlitzPlayer
pour l'occasion. Si vous voyez d'autres modifications utiles, effectuez-les. :)
Vous trouverez le code sur la branche P2C5, et la correction sur la branche P2C5-correction.
En résumé
Pour manipuler une constante et conserver la valeur de la classe courante, il faut y faire référence avec le mot clé
self
.Pour manipuler une constante et utiliser la valeur de la classe courante ou héritée si redéfinition, il faut y faire référence avec le mot clé
static
.
Vous avez toutes les clés pour représenter sous forme de classes des éléments de la vie de tous les jours, et imaginer des structures communes de code pour réduire la charge de maintenance et le risque d'erreur. Vous êtes prêt à vous initier à des structures de code qui vous permettront de lier vos classes entre elles.
Comment les lier autrement que par l’héritage ? Si vous êtes impatient de le découvrir, retrouvez-moi dans la prochaine partie – après un petit quiz, bien sûr !