1.Résumé
La RFC recommande d'ajouter 4 nouvelles déclarations de type scalaire : int, float, string et bool. Ces déclarations de type seront utilisées de manière cohérente avec le mécanisme d'origine de PHP. La RFC recommande également d'ajouter une nouvelle instruction facultative (declare(strict_type=1);) à chaque fichier PHP afin que tous les appels de fonction et les retours d'instructions dans le même fichier PHP aient une vérification de type scalaire "strictement contraint". De plus, après avoir activé des contraintes de type strictes, l'appel d'extensions ou de fonctions intégrées PHP générera une erreur de niveau E_RECOVERABLE_ERROR si l'analyse des paramètres échoue. Avec ces deux fonctionnalités, la RFC espère que l’écriture de PHP deviendra plus précise et documentée.
2. Détails
Déclaration de type scalaire :
Aucun nouveau ajout Le mot réservé. Int, float, string et bool seront reconnus comme des déclarations de type et il est interdit de les utiliser comme noms de classe/interface/trait, etc. Nouvelle déclaration de type scalaire utilisateur, implémentée via l'API interne Fast Parameter Parsing.
directive strict_types/declare()
Par défaut, tous les fichiers PHP sont en mode de vérification de type faible. La nouvelle directive declare spécifie la valeur de strict_types (1 ou 0). 1 indique un mode de vérification de type strict, qui s'applique aux appels de fonction et aux instructions return ; 0 indique un mode de vérification de type faible.
declare(strict_types=1) doit être la première instruction du fichier. Si cette instruction apparaît ailleurs dans le fichier, une erreur de compilation sera générée et le mode bloc est explicitement interdit.
est similaire à la directive encoding, mais contrairement à la directive ticks, la directive strict_types n'affecte que les fichiers spécifiés et n'affecte pas les autres fichiers qu'il inclut (via include, etc.). Cette directive est compilée au moment de l'exécution et ne peut pas être modifiée. La façon dont cela fonctionne consiste à définir un indicateur dans l'opcode afin que les appels de fonction et les vérifications de type de retour soient conformes aux contraintes de type.
Déclaration du type de paramètre
Cette directive affecte tous les appels de fonction, par exemple (mode de vérification stricte) :
<?php declare(strict_types=1); foo(); // strictly type-checked functioncall function foobar() { foo(); // strictly type-checked function call } class baz { function foobar() { foo(); // strictly type-checked function call } }
Comparaison (mode de validation faible)
<?php foo(); // weakly type-checked function call function foobar() { foo(); // weakly type-checked function call } class baz { function foobar() { foo(); // weakly type-checked function call } } ?>
Déclaration de type de retour :
la directive affectera les mêmes types de retour de toutes les fonctions sous le fichier. Par exemple (mode de validation strict) :
<?php declare(strict_types=1); function foobar(): int { return 1.0; // strictly type-checked return } class baz { function foobar(): int { return 1.0; // strictly type-checked return }} ?>
<?php function foobar(): int { return 1.0; // weakly type-checked return } class baz { function foobar(): int { return 1.0; // weakly type-checked return }} ?>
Comportement de vérification de type faible :
一个弱类型校验的函数调用,和PHP7之前的PHP版本是一致的(包括拓展和PHP内置函数)。通常,弱类型校验规则对于新的标量类型声明的处理是相同的,但是,唯一的例外是对NULL的处理。为了和我们现有类、调用、数组的类型声明保持一致,NULL不是默认的,除非它作为一个参数并且被显式赋值为NULL。
为了给不熟悉PHP现有的弱标量参数类型规则的读者,提供简短的总结。表格展示不同类型能够接受和转换的标量类型声明,NULL、arrays和resource不能接受标量类型声明,因此不在表格内。
*只有范围在PHP_INT_MIN和PHP_INT_MAX内的non-NaN float类型可以接受。(PHP7新增,可查看ZPP Failure on Overflow RFC)
?Non-numeric型字符串不被接受,Numeric型字符串跟随字符串的,也可以被接受,但是会产生一个notice。
?仅当它有toString方法时可以。
严格类型校验行为:
严格的类型校验调用拓展或者PHP内置函数,会改变zend_parse_parameters的行为。特别注意,失败的时候,它会产生E_RECOVERABLE_ERROR而不是E_WARNING。它遵循严格类型校验规则,而不是传统的弱类型校验规则。严格类型校验规则是非常直接的:只有当类型和指定类型声明匹配,它才会接受,否则拒绝。
有一个例外的是,宽泛类型转换是允许int变为float的,也就是说参数如果被声明为float类型,但是它仍然可以接受int参数。
<?php declare(strict_types=1); function add(float $a, float $b): float { return $a + $b;} add(1, 2); // float(3) ?>
在这种场景下,我们传递一个int参数给到定义接受float的函数,这个参数将会被转换为float。除此之外的转换,都是不被允许的。
三、例子:
让我们创建一个函数,让2个数相加。
add.php <?php function add(int $a, int $b): int { return $a + $b;} ?>
如果在分开的文件,我们可以调用add函数通过弱类型的方式
<?php require "add.php"; var_dump(add(1,2)); // int(3) // floats are truncated by default var_dump(add(1.5,2.5)); // int(3) //strings convert if there's a number part var_dump(add("1","2")); // int(3) ?>
默认情况下,弱类型声明允许使用转换,传递进去的值会被转换。
<?php require "add.php"; var_dump(add("1foo", "2")); // int(3) // Notice: A non well formed numeric value encountered
但是,通过可选择指令declare开启严格类型校验后,在这个场景下,相同的调用将会失败。
<?php declare(strict_types=1); require "add.php"; var_dump(add(1,2)); // int(3) var_dump(add(1.5,2.5)); // int(3) // Catchable fatal error: Argument 1 passed to add() must be of the type integer, float given
指令影响同一个文件下的所有函数调用,不管这个被调函数是否在这个文件内定义的,都会采用严格类型校验模式。
<?php declare(strict_types=1); $foo = substr(52,1); // Catchable fatal error: substr() expects parameter 1 to be string, integer given
标量类型声明也可以用于返回值的严格类型校验:
<?php function foobar(): int { return 1.0; } var_dump(foobar());// int(1)
在弱类型模式下,float被转为integer。
<?php declare(strict_types=1); function foobar(): int { return 1.0; } var_dump(foobar()); //Catchable fatal error: Return value of foobar() must be of the type integer,float returned
四、背景和理论基础
历史
PHP从PHP5.0开始已经有对支持class和interface参数类型声明,PHP5.1支持array以及PHP5.4支持callable。这些类型声明让PHP在执行的时候传入正确的参数,让函数签名具有更多的信息。
先前曾经想添加标量类型声明,例如Scalar Type Hints with Casts RFC,因为各种原因失败了:
(1)类型转换和校验机制,对于拓展和PHP内置函数不匹配。
(2)它遵循一个弱类型方法。
(3)它的“严格”弱类型修改尝试,既没有满足严格类型的粉丝期望,也没有满足弱类型的粉丝。
这个RFC尝试解决全部问题。
弱类型和强类型
在现代编程语言的实际应用中,有三种主要的方法去检查参数和返回值的类型:
(1)全严格类型检查(也就是不会有类型转换发生)。例如F#、GO、Haskell、Rust和Facebook的Hack的用法。
(2)广泛原始类型检查(“安全”的类型转换会发生)。例如Java、D和Pascal。他们允许广泛原始类型转换(隐式转换),也就是说,一个8-bit的integer可以根据函数参数需要,被隐形转换为一个16-bit的integer,而且int也可以被转换为float的浮点数。其他类型的隐式转换则不被允许。
(3)弱类型检查(允许所有类型转换,可能会引起警告),它被有限制地使用在C、C#、C++和Visual Basic中。它们尝试尽可能“不失败”,完成一次转换。
PHP在zend_parse_parameters的标量内部处理机制是采用了弱类型模式。PHP的对象处理机制采用了广泛类型检查方式,并不追求精确匹配和转换。
每个方法各有其优缺点。
这个提案中,默认采用弱类型校验机制,同时追加一个开关,允许转换为广泛类型校验机制(也就是严格类型校验机制)。
Pourquoi soutenir les deux ?
Jusqu'à présent, la plupart des partisans des déclarations de type scalaire exigent la prise en charge à la fois de la vérification de type stricte et du typage faible. La vérification ne se limite pas soutenir l’un d’entre eux. Cette RFC fait de la vérification de type faible le comportement par défaut et ajoute une directive facultative pour utiliser la vérification de type stricte (dans le même fichier). De nombreuses raisons justifient ce choix.
Une grande partie de la communauté PHP semble aimer Quan Type statique. Cependant, l'ajout d'une vérification de type stricte aux déclarations de type scalaire entraînera quelques problèmes :
(1) provoquant des incohérences évidentes : extensions et PHP construits Les fonctions -in utilisent une vérification de type faible pour les paramètres de type scalaire, cependant, les fonctions PHP de l'utilisateur utiliseront une vérification de type stricte.
(2) Un nombre considérable de personnes préfèrent une vérification de type faible et ne sont pas d'accord avec cette proposition, et peuvent bloquer sa mise en œuvre .
(3) Le code déjà existant utilise les types faibles de PHP, qui seront affectés. Si des fonctions sont nécessaires pour ajouter des déclarations de type scalaire aux paramètres, cela augmentera considérablement la complexité des bases de code existantes, en particulier pour les fichiers de bibliothèque.
Il y a encore pas mal de gens ici qui aiment la vérification de type faible, cependant, l'ajout de déclarations de vérification de type strictes et l'ajout de déclarations de vérification de type faibles entraîneront certains problèmes :
(1) La plupart des gens qui préférer une vérification de type stricte n'aimera pas cette proposition et bloquera sa mise en œuvre.
(2) Limiter les opportunités d'analyse statique. (Peut-être que cela signifie des opportunités d'optimisation)
(3) Cela masquera certains bugs de perte de données dans la conversion automatique de type.
La troisième solution a été proposée, qui consiste à ajouter une syntaxe pour distinguer entre la déclaration de type faible et la déclaration de type stricte. Cela entraînera également quelques problèmes :
(1) Les personnes qui n'aiment pas les types faibles et la vérification de type stricte seront forcées d'accéder aux bibliothèques. définis comme strictement typés ou faiblement typés sont traités séparément.
(2) Comme l'ajout de déclarations strictes, cela sera également incompatible avec l'expansion de l'implémentation originale du type faible et du PHP intégré. dans les fonctions.
Afin de résoudre les problèmes provoqués par ces trois solutions, cette RFC propose une quatrième solution : chaque fichier est strictement défini ou faible vérification de type. Il apporte les avantages suivants :
(1) Les gens peuvent choisir la vérification de type qui leur convient, c'est-à-dire ce schéma J'espère satisfaire les camps de vérification de type stricts et faibles.
(2) L'API ne sera pas obligée de s'adapter à un certain modèle de déclaration de type.
(3) Étant donné que le fichier utilise par défaut un schéma de vérification de type faible, la base de code existante peut être modifiée sans détruire le code structure., ajoutez une déclaration de type scalaire. Il est également possible que la base de code ajoute des déclarations de type de manière incrémentielle, ou uniquement pour certains modules.
(4) Une seule syntaxe est nécessaire pour définir une déclaration de type scalaire.
(5) Les personnes qui préfèrent une vérification de type stricte utilisent généralement non seulement cette fonctionnalité dans les fonctions définies par l'utilisateur, mais également dans extensions et fonctions intégrées PHP. En d’autres termes, les utilisateurs de PHP bénéficieront d’un mécanisme unifié sans la contradiction d’une déclaration scalaire stricte.
(6) En mode de vérification de type strict, le niveau d'erreur d'échec de vérification de type généré par l'expansion et les fonctions intégrées PHP, Il sera cohérent avec celui généré par la fonction définie par l'utilisateur, qui est E_RECOVERABLE_ERROR.
(7) Il permet d'intégrer de manière transparente du code strictement typé et faiblement typé dans une base de code unique.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!