Parcours
/
PHP
PHP
/
Programme
/
Covariance et contravariance
Co

Covariance et contravariance en PHP

{one: "%{count} exercice", many: "%{count} d'exercices", other: "%{count} exercices"}

À propos de Covariance et contravariance

Covariance et contravariance

Lorsqu'on utilise des types union, il faut connaître les notions de covariance et de contravariance. Elles sont expliquées dans la documentation, mais ces exemples pourront peut-être t'aider.

Imaginons une classe Calculator qui utilise une fonction comme dans l'exemple ci-dessus :

class Calculator
{
    public function add(int|float $x, int|float $y): int|float
    {
        return $x + $y;
    }
}

Si tu étends cette classe, tu peux modifier les types des paramètres et les types de retour, mais uniquement en suivant les règles ci-dessous.

D'abord, les types des paramètres peuvent être « plus larges », c'est-à-dire accepter davantage de types que la classe de base. Par exemple, ceci est autorisé :

class StringCalc extends Foo
{
    public function add(float|int|string $x, float|int|string $y): int|float
    {
        if (!is_numeric($x) || !is_numeric($y)) {
            throw new \InvalidArgumentException('$x and $y must be numeric');
        }
        return $x + $y;
    }
}

On pourrait remplacer un Calculator d'origine par StringCalc, et tout continuerait de fonctionner. L'appelant pourrait toujours passer les float ou int qu'il pouvait envoyer à l'origine, et le type de retour serait toujours le int ou le float attendu.

De la même manière, tu peux réduire le type de retour, c'est-à-dire le rendre plus spécifique en retirant des types de l'union. Cet exemple tire parti de cette règle :

class IntCalc extends Calculator
{
    public function add(float|int $x, float|int $y) : int
    {
        return (int)($x + $y);
    }
}

Comme IntCalc accepte toujours int et float, il peut toujours remplacer un Calculator d'origine sans problème.

Pour en savoir plus sur ce qui est autorisé et ce qui ne l'est pas, tu peux consulter le principe de substitution de Liskov (LSP).

Pour un exemple de ce qui ne serait pas autorisé, regarde la classe InvalidCalc ci-dessous :

class InvalidCalc extends Calculator
{
    public function add(int $x, int $y) : int|float|string
    {
        // Declaration must be compatible with Calculator->add(x: float|int, y: float|int)

        // Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
    }
}

La classe InvalidCalc ne compilera pas et présente deux problèmes. En réduisant ce qui est autorisé comme paramètre, InvalidCalc ne pourrait pas remplacer le Calculator d'origine, car elle n'accepte plus float. De plus, elle peut désormais renvoyer une string, ce qui ne fait pas non plus partie des types de retour autorisés de la classe d'origine.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet