Recherche…


Qu'est-ce que la jonglerie de type?

PHP est un langage vaguement typé. Cela signifie que, par défaut, les opérandes d'une expression ne sont pas du même type (ou compatibles). Par exemple, vous pouvez ajouter un numéro à une chaîne et vous attendre à ce qu'il fonctionne.

var_dump ("This is example number " . 1);

Le résultat sera:

string (24) "Ceci est l'exemple numéro 1"

PHP accomplit cela en lançant automatiquement des types de variables incompatibles dans des types permettant à l'opération demandée d'avoir lieu. Dans le cas ci-dessus, il convertit le littéral entier 1 en une chaîne, ce qui signifie qu'il peut être concaténé sur le littéral chaîne précédent. C'est ce qu'on appelle le jonglage de type. Ceci est une fonctionnalité très puissante de PHP, mais c'est aussi une fonctionnalité qui peut vous amener à beaucoup de tracas si vous n'en avez pas conscience et peut même entraîner des problèmes de sécurité.

Considérer ce qui suit:

if (1 == $variable) {
    // do something
}

L'intention semble être que le programmeur vérifie qu'une variable a la valeur 1. Mais que se passe-t-il si $ variable a une valeur de "1 et demi" à la place? La réponse pourrait vous surprendre.

$variable = "1 and a half";
var_dump (1 == $variable);

Le résultat est:

bool (vrai)

Pourquoi est-ce arrivé? C'est parce que PHP s'est rendu compte que la chaîne "1 et demi" n'est pas un entier, mais il doit être pour le comparer à un entier 1. Au lieu d'échouer, PHP lance la jonglerie et tente de convertir la variable en entier. Cela se fait en prenant tous les caractères au début de la chaîne qui peuvent être convertis en entier et en les lançant. Il s'arrête dès qu'il rencontre un personnage qui ne peut pas être traité comme un nombre. Par conséquent, "1 et demi" est converti en entier 1.

Certes, ceci est un exemple très artificiel, mais il sert à démontrer le problème. Les quelques exemples suivants couvriront certains cas où j'ai rencontré des erreurs causées par le type de jonglage qui s'est produit dans un logiciel réel.

Lecture d'un fichier

Lors de la lecture d'un fichier, nous voulons être en mesure de savoir quand nous avons atteint la fin de ce fichier. Sachant que fgets() renvoie false à la fin du fichier, nous pouvons l'utiliser comme condition pour une boucle. Toutefois, si les données renvoyées par la dernière lecture se révèlent être des valeurs booléennes false , la boucle de lecture du fichier peut se terminer prématurément.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

Si le fichier en cours de lecture contient une ligne vide, le while en boucle sera terminée à ce moment - là, parce que la chaîne vide est évaluée comme booléen false .

Au lieu de cela, nous pouvons vérifier explicitement la valeur booléenne false , en utilisant des opérateurs d'égalité stricte :

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

Notez que ceci est un exemple artificiel; Dans la vraie vie, nous utiliserions la boucle suivante:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

Ou remplacer le tout par:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

Changer de surprise

Les instructions de commutation utilisent une comparaison non stricte pour déterminer les correspondances. Cela peut entraîner de mauvaises surprises . Par exemple, considérez l'instruction suivante:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

Ceci est une instruction très simple et fonctionne comme prévu lorsque $name est une chaîne, mais peut provoquer des problèmes sinon. Par exemple, si $name est un entier 0 , la jonglerie se produira pendant la comparaison. Cependant, c'est la valeur littérale dans l'instruction case qui est jonglée, pas la condition dans l'instruction switch. La chaîne "input 1" est convertie en entier 0 qui correspond à la valeur d'entrée de l'entier 0 . Le résultat de ceci est que si vous fournissez une valeur de 0 , le premier cas est toujours exécuté.

Il y a quelques solutions à ce problème:

Coulée explicite

La valeur peut être transtypée en chaîne avant la comparaison:

switch ((string)$name) {
...
}

Ou une fonction connue pour renvoyer une chaîne peut également être utilisée:

switch (strval($name)) {
...
}

Ces deux méthodes garantissent que la valeur est du même type que la valeur dans les instructions de case .

Eviter l' switch

L'utilisation d'une instruction if nous permettra de contrôler la manière dont la comparaison est effectuée, ce qui nous permet d'utiliser des opérateurs de comparaison stricts :

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

Dactylographie stricte

Depuis PHP 7.0, certains des effets néfastes de la jonglerie de type peuvent être atténués par un typage strict . En incluant cette declare déclaration comme la première ligne du fichier, PHP appliquera les déclarations de type de paramètres et retourner les déclarations de type en lançant une TypeError exception.

declare(strict_types=1);

Par exemple, ce code, utilisant des définitions de type de paramètre, lancera une exception TypeError de type TypeError lors de l'exécution:

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

De même, ce code utilise une déclaration de type retour; il jettera aussi une exception s'il essaie de renvoyer autre chose qu'un entier:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow