Cette RFC recommande d'ajouter 4 nouvelles déclarations de type scalaire : int, float, string et bool. Ces déclarations de type seront les mêmes que celles d'origine de PHP. . Le mécanisme maintient une utilisation cohérente. 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.
Tutoriel recommandé : "Tutoriel PHP"
Déclaration de type scalaire :
Aucun nouveau mot réservé ajouté. 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.
Semblable à la directive encoding, mais différente de la directive ticks, la directive strict_types n'affecte que le fichier spécifié et n'affectera 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 instruction affecte tous les appels de fonction, par exemple (mode de vérification strict) :
<?php declare(strict_types=1); foo(); // strictly type-checked function call function foobar() { foo(); // strictly type-checked function call } class baz { function foobar() { foo(); // strictly type-checked function call } }
Contraste (mode de vérification 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 du type de retour : La directive
affectera les types de retour de toutes les fonctions dans le même 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 :
Un appel de fonction de vérification de type faible, qui est cohérent avec les versions PHP antérieures à PHP7 (y compris les extensions et les fonctions intégrées PHP). En général, les règles de vérification de type faible sont les mêmes pour les nouvelles déclarations de type scalaire, mais la seule exception est le traitement de NULL. Pour être cohérent avec nos déclarations de types de classe, d'appel et de tableau existantes, NULL n'est pas la valeur par défaut à moins qu'elle ne soit passée en paramètre et explicitement affectée à NULL.
Fournir un bref résumé aux lecteurs qui ne sont pas familiers avec les règles existantes de PHP pour les types de paramètres scalaires faibles. Le tableau montre les déclarations de type scalaire que différents types peuvent accepter et convertir. NULL, les tableaux et les ressources ne peuvent pas accepter les déclarations de type scalaire, ils ne sont donc pas inclus dans le tableau.
* Seuls les types float non NaN dans la plage PHP_INT_MIN et PHP_INT_MAX sont acceptés. (Nouveau dans PHP7, voir ZPP Failure on Overflow RFC)
?Les chaînes non numériques ne sont pas acceptées. Les chaînes numériques qui suivent les chaînes peuvent également être acceptées, mais une notification sera générée.
? Seulement s'il possède la méthode __toString.
Comportement de vérification de type strict :
Une vérification de type stricte lors de l'appel d'extensions ou de fonctions intégrées PHP modifiera le comportement de zend_parse_parameters. Notez en particulier qu'en cas d'échec, il générera E_RECOVERABLE_ERROR au lieu de E_WARNING. Il suit des règles strictes de vérification de type au lieu des règles traditionnelles de vérification de type faible. Les règles de vérification de type stricte sont très simples : ce n'est que si le type correspond à la déclaration de type spécifiée qu'il sera accepté, sinon il sera rejeté.
Une exception est que la conversion de type large permet de changer int en float, c'est-à-dire que si le paramètre est déclaré comme type float, il peut toujours accepter les paramètres int.
<?php declare(strict_types=1); function add(float $a, float $b): float { return $a + $b; } add(1, 2); // float(3)
Dans ce scénario, nous passons un paramètre int à la fonction définie pour accepter float, et ce paramètre sera converti en float. Les conversions autres que celles-ci ne sont pas autorisées.
Créons une fonction pour ajouter 2 nombres.
add.php
<?php function add(int $a, int $b): int { return $a + $b; }
Si dans un fichier séparé, on peut appeler la fonction add en tapant faiblement
<?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)
Par défaut, la déclaration de type faible permet l'utilisation de la conversion, La valeur transmise sera convertie.
<?php require "add.php"; var_dump(add("1 foo", "2")); // int(3) // Notice: A non well formed numeric value encountered
Cependant, après avoir activé la vérification de type stricte via la directive de déclaration facultative, le même appel échouera dans ce scénario. La directive
<?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
affecte tous les appels de fonction dans le même fichier. Que la fonction appelée soit définie ou non dans ce fichier, le mode de vérification de type strict sera utilisé.
<?php declare(strict_types=1); $foo = substr(52, 1); // Catchable fatal error: substr() expects parameter 1 to be string, integer given
Les déclarations de type scalaire peuvent également être utilisées pour une vérification de type stricte des valeurs de retour :
<?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的对象处理机制采用了广泛类型检查方式,并不追求精确匹配和转换。
每个方法各有其优缺点。
这个提案中,默认采用弱类型校验机制,同时追加一个开关,允许转换为广泛类型校验机制(也就是严格类型校验机制)。
为什么两者都支持?
目前为止,大部分的标量类型声明的拥护者都要求同时支持严格类型校验和弱类型校验,并非仅仅支持其中一种。这份RFC,使得弱类型校验为默认行为,同时,添加一个可选的指令来使用严格类型校验(同一个文件中)。在这个选择的背后,有很多个原因。
PHP社区很大一部分人看起来很喜欢全静态类型。但是,添加严格类型校验的标量类型声明将会引起一些问题:
(1)引起明显的不一致性:拓展和PHP内置函数对标量类型参数使用弱类型校验,但是,用户的PHP函数将会使用严格类型校验。
(2)相当一部分人更喜欢弱类型校验,并不赞同这个提案,他们可能会阻止它的实施。
(3)已经存在的代码使用了PHP的弱类型,它会受到影响。如果要求函数添加标量类型声明到参数上,对于现有的代码库,这将大大增加复杂性,特别是对于库文件。
这里仍然有相当于一部分人是喜欢弱类型校验的,但是,添加严格类型校验声明和添加弱类型校验声明都会引起一些问题:
(1)大部分倾向于严格类型校验的人将不会喜欢这个提案,然后阻止它的实施。
(2)限制静态解析的机会。(可能是说,优化的机会)
(3)它会隐藏一些在类型自动转换中数据丢失的bug。
第三种方案被提出来了,就是添加区分弱类型和严格类型声明的语法。它也会带来一些问题:
(1)不喜欢弱类型和严格类型校验的人,会被强迫分别处理被定义为严格类型或者弱类型校验的库。
(2)像添加严格声明一样,这个也将和原来弱类型实现的拓展和PHP内置函数无法保持一致。
为了解决这三种方案带来的问题,这个RFC提出了第四种方案:每个文件各自定义严格或者弱类型校验。它带来了以下好处:
(1)人们可以选择适合他们的类型校验,也就是说,这个方案希望同时满足严格和弱类型校验两个阵营。
(2)API不会被强制适应某个类型声明模式。
(3)因为文件默认使用弱类型校验方案,已经存在的代码库,可以在不破坏代码结构的情况下,添加标量类型声明。也可以让代码库逐步添加类型声明,或者仅部分模块添加。
(4)只需要一个单一语法,就可以定义标量类型声明。
(5)更喜欢严格类型校验的人,通常,不仅将这个特性使用在用户定义的函数,同时也使用在拓展和PHP内置函数中。也就是说,PHP使用者会得到一个统一机制,而不会产生严格标量声明的矛盾。
(6) En mode de vérification de type strict, le niveau d'erreur d'échec de vérification de type généré par les extensions et les fonctions intégrées PHP sera cohérent avec celui généré par les fonctions définies par l'utilisateur, qui sont toutes deux 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!