Home > Article > Backend Development > PHP7 scalar type declaration RFC detailed explanation
This RFC recommends adding 4 new scalar type declarations: int, float, string and bool. These type declarations will be the same as PHP’s original ones. The mechanism maintains consistent usage. RFC also recommends adding a new optional instruction (declare(strict_type=1);) to each PHP file so that all function calls and statement returns in the same PHP file have a "strictly constrained" scalar type. Statement check. In addition, after turning on strict type constraints, calling extensions or PHP built-in functions will generate an E_RECOVERABLE_ERROR level error if parameter parsing fails. With these two features, the RFC hopes that writing PHP will become more accurate and documented.
Recommended tutorial: "PHP Tutorial"
Scalar type declaration:
No new reserved words added . Int, float, string and bool will be recognized as type declarations and are prohibited from being used as names for class/interface/trait, etc. New user scalar type declaration, implemented through the internal Fast Parameter Parsing API.
strict_types/declare() directive
By default, all PHP files are in weak type checking mode. The new declare directive specifies the value of strict_types (1 or 0). 1 indicates strict type checking mode, which applies to function calls and return statements; 0 indicates weak type checking mode.
declare(strict_types=1) must be the first statement of the file. If this statement appears elsewhere in the file, a compilation error will be generated, and block mode is explicitly prohibited.
Similar to the encoding directive, but different from the ticks directive, the strict_types directive only affects the specified file and will not affect other files included by it (through include, etc.). This directive is compiled at runtime and cannot be modified. The way it works is to set a flag in the opcode so that function calls and return type checks comply with type constraints.
Parameter type declaration
This instruction affects all function calls, for example (strict verification mode):
<?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 } }
Contrast (weak verification mode )
<?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 } }
Return type declaration:
The directive will affect the return type of all functions in the same file. For example (strict verification mode):
<?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 } }
Weak type checking behavior:
A weak type checking function call is consistent with PHP versions before PHP7 (including extensions and PHP built-in functions). In general, the weak type checking rules are the same for new scalar type declarations, but the only exception is the treatment of NULL. To be consistent with our existing class, call, and array type declarations, NULL is not the default unless it is passed as a parameter and is explicitly assigned to NULL.
To provide a brief summary for readers unfamiliar with PHP's existing rules for weak scalar parameter types. The table shows the scalar type declarations that different types can accept and convert. NULL, arrays, and resources cannot accept scalar type declarations, so they are not included in the table.
*Only non-NaN float types in the range PHP_INT_MIN and PHP_INT_MAX are accepted. (New in PHP7, see ZPP Failure on Overflow RFC)
?Non-numeric strings are not accepted. Numeric strings following strings can also be accepted, but a notice will be generated.
?Only if it has __toString method.
Strict type checking behavior:
Strict type checking calls extensions or PHP built-in functions, which will change the behavior of zend_parse_parameters. Special note that when it fails, it will generate E_RECOVERABLE_ERROR instead of E_WARNING. It follows strict type checking rules instead of traditional weak type checking rules. The rules of strict type checking are very straightforward: only if the type matches the specified type declaration, it will be accepted, otherwise it will be rejected.
One exception is that wide type conversion allows int to be changed to float, that is to say, if the parameter is declared as float type, it can still accept int parameters.
<?php declare(strict_types=1); function add(float $a, float $b): float { return $a + $b; } add(1, 2); // float(3)
In this scenario, we pass an int parameter to the function defined to accept float, and this parameter will be converted to float. Conversions other than this are not allowed.
Let us create a function to add 2 numbers.
add.php
<?php function add(int $a, int $b): int { return $a + $b; }
If in a separate file, we can call the add function by weakly typing
<?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)
By default, weak type declaration allows the use of conversion, The value passed in will be converted.
<?php require "add.php"; var_dump(add("1 foo", "2")); // int(3) // Notice: A non well formed numeric value encountered
However, after turning on strict type checking through the optional declare directive, the same call will fail in this scenario. The
<?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
directive affects all function calls under the same file. Regardless of whether the called function is defined in this file, strict type checking mode will be used.
<?php declare(strict_types=1); $foo = substr(52, 1); // Catchable fatal error: substr() expects parameter 1 to be string, integer given
Scalar type declarations can also be used for strict type checking of return values:
<?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) In strict type checking mode, the error level of type verification failure generated by extensions and PHP built-in functions will be consistent with that generated by user-defined functions, both of which are E_RECOVERABLE_ERROR.
(7) It allows strictly typed and weakly typed code to be seamlessly integrated in a single code base.
The above is the detailed content of PHP7 scalar type declaration RFC detailed explanation. For more information, please follow other related articles on the PHP Chinese website!