Pour le meilleur ou le pire, lâche-frappe est « The Way PHP ». La plupart des constructions intégrées, et la plupart des constructions de langage, fonctionneront sur tous les types que vous leur donnez - silencieusement (et souvent dangereusement) les coulant dans les coulisses pour faire des choses (sorte de) s'emboîtent.
Venant moi-même d'un arrière-plan Java/C/C++, le modèle lâche de PHP a toujours été une source de frustration pour moi. Mais au fil des années, j'ai découvert que si je devais écrire PHP, je pourrais mieux l'utiliser (c'est-à-dire un code plus propre, plus sûr et plus testable) en adoptant le "relâchement" de PHP plutôt que de le combattre. et je finis un singe plus heureux à cause de cela.
Le moulage est vraiment fondamental pour ma technique - et (IMHO) c'est la seule façon de construire de manière cohérente du code PHP propre et lisible qui gère les arguments de type mixte de manière bien compréhensible, testable et déterministe.
Le point principal (que vous comprenez bien aussi) est que, en PHP, vous ne pouvez pas simplement supposer qu'un argument est le type attendu. Cela peut avoir de graves conséquences que vous ne risquez pas d'attendre avant que votre application ne soit mise en production.
Pour illustrer ce point:
<?php
function displayRoomCount($numBoys, $numGirls) {
// we'll assume both args are int
// check boundary conditions
if(($numBoys < 0) || ($numGirls < 0)) throw new Exception('argument out of range');
// perform the specified logic
$total = $numBoys + $numGirls;
print("{$total} people: {$numBoys} boys, and {$numGirls} girls \n");
}
displayRoomCount(0, 0); // (ok) prints: "0 people: 0 boys, and 0 girls"
displayRoomCount(-10, 20); // (ok) throws an exception
displayRoomCount("asdf", 10); // (wrong!) prints: "10 people: asdf boys, and 10 girls"
Une approche pour résoudre consiste à limiter les types que la fonction peut accepter, lancer une exception en cas de détection d'un type non valide. D'autres ont déjà mentionné cette approche. Il fait bon appel à mon esthétique Java/C/C++, et j'ai suivi cette approche en PHP depuis des années et des années. En bref, il n'y a rien de mal à cela, mais cela va à l'encontre de "The PHP Way", et après un certain temps, cela commence à avoir l'impression de nager en amont. En variante, le moulage fournit une manière simple et propre de s'assurer que la fonction se comporte de façon déterministe pour toutes les entrées possibles, sans avoir à écrire une logique spécifique pour gérer chaque type différent.
En utilisant la coulée, notre exemple devient:
<?php
function displayRoomCount($numBoys, $numGirls) {
// we cast to ensure that we have the types we expect
$numBoys = (int)$numBoys;
$numGirls = (int)$numGirls;
// check boundary conditions
if(($numBoys < 0) || ($numGirls < 0)) throw new Exception('argument out of range');
// perform the specified logic
$total = $numBoys + $numGirls;
print("{$total} people: {$numBoys} boys, and {$numGirls} girls \n");
}
displayRoomCount("asdf", 10); // (ok now!) prints: "10 people: 0 boys, and 10 girls"
La fonction se comporte maintenant comme prévu. En fait, il est facile de montrer que le comportement de la fonction est maintenant bien défini pour tous entrées possibles. C'est parce que l'opération de moulage est bien définie pour toutes les entrées possibles; les moulages s'assurent que nous travaillons toujours avec des nombres entiers; et le reste de la fonction est écrit de manière à être bien défini pour tous les entiers possibles.
Rules for type-casting in PHP are documented here, (voir les liens spécifiques au type au milieu de la page - par exemple: "Conversion en nombre entier").
Cette approche présente l'avantage supplémentaire que la fonction se comporte désormais de manière cohérente avec les autres composants PHP et les constructions de langage. Par exemple:
// assume $db_row read from a database of some sort
displayRoomCount($db_row['boys'], $db_row['girls']);
fonctionnera très bien, malgré le fait que $db_row['boys']
et $db_row['girls']
sont en fait des chaînes qui contiennent des valeurs numériques. Ceci est cohérent avec la façon dont le développeur PHP moyen (qui ne connait pas C, C++ ou Java) s'attend à ce qu'il fonctionne.
En ce qui concerne la coulée des valeurs de retour: il y a très peu de point à le faire, à moins que vous savez que vous avez une variable de type mixte potentiellement, et que vous voulez toujours veiller à ce que la valeur de retour est un type spécifique. C'est plus souvent le cas aux points intermédiaires du code, plutôt qu'au point où vous revenez d'une fonction.
Un exemple pratique:
<?php
function getParam($name, $idx=0) {
$name = (string)$name;
$idx = (int)$idx;
if($name==='') return null;
if($idx<0) $idx=0;
// $_REQUEST[$name] could be null, or string, or array
// this depends on the web request that came in. Our use of
// the array cast here, lets us write generic logic to deal with them all
//
$param = (array)$_REQUEST[$name];
if(count($param) <= $idx) return null;
return $param[$idx];
}
// here, the cast is used to ensure that we always get a string
// even if "fullName" was missing from the request, the cast will convert
// the returned NULL value into an empty string.
$full_name = (string)getParam("fullName");
Vous voyez l'idée.
Il y a quelques années Gotcha pour être au courant du mécanisme de coulée de
PHP est pas assez intelligent pour optimiser la "no-op" casting. Donc, la conversion provoque toujours une copie de la variable à effectuer. Dans la plupart des cas, ce n'est pas un problème, mais si vous utilisez régulièrement cette approche, vous devriez garder cela à l'esprit. Pour cette raison, la diffusion peut provoquer des problèmes inattendus avec les références et les grands tableaux. Voir PHP Bug Report #50894 pour plus de détails.
En PHP, un nombre entier qui est trop grand (ou trop petit) pour représenter un type entier, sera automatiquement représenté comme un flottant (ou un double, si nécessaire). Cela signifie que le résultat de ($big_int + $big_int)
peut effectivement être un flottant, et si vous le lancez dans un int, le résultat sera du charabia. Donc, si vous construisez des fonctions qui doivent fonctionner sur de grands nombres entiers, vous devriez garder cela à l'esprit, et probablement envisager une autre approche.
Désolé pour le long courrier, mais il est un sujet que j'ai réfléchi en profondeur, et au fil des ans, j'ai accumulé un peu de connaissances (et d'opinion) à ce sujet. En l'exposant ici, j'espère que quelqu'un le trouvera utile.
Vous pouvez penser que le typage faible (comme en PHP, javascript, Smalltalk, c, C++, etc) est une faille, mais cela fait partie de la conception de PHP, et donc peu susceptible de changer. C'est aussi l'une des caractéristiques qui font de PHP un langage de type c. – GZipp
@GZopp: C et C++ ne sont pas des langages faiblement typés. –
Vous pouvez réellement exiger que les arguments soient d'un certain type, mais cela ne fonctionne qu'avec des classes et des tableaux, pas d'autres types intégrés. http://www.php.net/manual/fr/functions.arguments.php#98425 – mwhite