• 10 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

Ce cours existe en livre papier.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 21/08/2023

Gérez le comportement d’une classe parente

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);

Tester ce code.

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 classe  Admin  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éthode  setStatus  fait référence aux constantes se trouvant dans la classe User. 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);

Tester ce code.

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 ! 

Exemple de certificat de réussite
Exemple de certificat de réussite