PHP中开启严格模式需要在文件最开始添加declare(strict_types = 1)
,显示声明,PHP默认是弱类型校验,而且该声明仅对当前文件有效,其他文件include
或require
时需要重新在那个文件声明。
看几个例子
两个数相加函数:
function foo (int $a, int $b): int {
return $a + $b;
}
var_dump(foo(1.0, 2.5)); // 结果是3
var_dump(foo('1', 2)); // 3
在默认若类型下,传过去的float
参数被截断成int
类型,同样的,字符串在弱模式下也被自动转成了int
,再来对比下严格模式:
declare(strict_types=1);
function foo (int $a, int $b): int {
return $a + $b;
}
var_dump(foo(1.0, 2.5)); // 报错
var_dump(foo('1', 2)); // 报错
// Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type integer, float given, called in /in/JQAi6 on line 10 and defined in /in/JQAi6:6
直接报错了,说foo()
只接受int
类型,而传过去是float
。
弱模式下,返回值类型也会被转换:
function foo (): int {
$r = 1.0;
return $r;
}
var_dump(foo()); // 1
严格模式下会报错:
declare(strict_types=1);
function foo (): int {
$r = 1.0;
return $r;
}
var_dump(foo()); // 报错
// Fatal error: Uncaught TypeError: Return value of foo() must be of the type int, float returned in /in/pRWIl:7
历史
PHP从PHP5.0开始已经有对支持class
和interface
参数类型声明,PHP5.1支持array
以及PHP5.4支持callable
。这些类型声明让PHP在执行的时候传入正确的参数,让函数签名具有更多的信息。
先前曾经想添加标量类型声明,例如Scalar Type Hints with Casts RFC,因为各种原因失败了:
-
类型转换和校验机制,对于拓展和PHP内置函数不匹配
-
它遵循一个弱类型方法
-
它的“严格”弱类型修改尝试,既没有满足严格类型的粉丝期望,也没有满足弱类型的粉丝
这个RFC尝试解决全部问题。
弱类型和强类型
在现代编程语言的实际应用中,有三种主要的方法去检查参数和返回值的类型:
-
全严格类型检查(也就是不会有类型转换发生)。例如
F#
、GO
、Haskell
、Rust
和Facebook的Hack
的用法 -
广泛原始类型检查(“安全”的类型转换会发生)。例如
Java
、D
和Pascal
。他们允许广泛原始类型转换(隐式转换),也就是说,一个8-bit的integer可以根据函数参数需要,被隐形转换为一个16-bit的integer,而且int也可以被转换为float的浮点数。其他类型的隐式转换则不被允许 -
弱类型检查(允许所有类型转换,可能会引起警告),它被有限制地使用在
C
、C#
、C++
和Visual Basic
中。它们尝试尽可能“不失败”,完成一次转换
PHP在zend_parse_parameters
的标量内部处理机制是采用了弱类型模式。PHP的对象处理机制采用了广泛类型检查方式,并不追求精确匹配和转换。每个方法各有其优缺点
这个提案中,默认采用弱类型校验机制,同时追加一个开关,允许转换为广泛类型校验机制(也就是严格类型校验机制)
为什么两者都支持?
目前为止,大部分的标量类型声明的拥护者都要求同时支持严格类型校验和弱类型校验,并非仅仅支持其中一种。这份RFC,使得弱类型校验为默认行为,同时,添加一个可选的指令来使用严格类型校验(同一个文件中),在这个选择的背后,有很多个原因。
PHP社区很大一部分人看起来很喜欢全静态类型。但是,添加严格类型校验的标量类型声明将会引起一些问题:
-
引起明显的不一致性:拓展和PHP内置函数对标量类型参数使用弱类型校验,但是,用户的PHP函数将会使用严格类型校验
-
相当一部分人更喜欢弱类型校验,并不赞同这个提案,他们可能会阻止它的实施
-
已经存在的代码使用了PHP的弱类型,它会受到影响。如果要求函数添加标量类型声明到参数上,对于现有的代码库,这将大大增加复杂性,特别是对于库文件
这里仍然有相当于一部分人是喜欢弱类型校验的,但是,添加严格类型校验声明和添加弱类型校验声明都会引起一些问题:
-
大部分倾向于严格类型校验的人将不会喜欢这个提案,然后阻止它的实施
-
限制静态解析的机会
-
它会隐藏一些在类型自动转换中数据丢失的bug
第三种方案被提出来了,就是添加区分弱类型和严格类型声明的语法。它也会带来一些问题:
-
不喜欢弱类型和严格类型校验的人,会被强迫分别处理被定义为严格类型或者弱类型校验的库
-
像添加严格声明一样,这个也将和原来弱类型实现的拓展和PHP内置函数无法保持一致
为了解决这三种方案带来的问题,这个RFC提出了第四种方案:每个文件各自定义严格或者弱类型校验。它带来了以下好处:
-
人们可以选择适合他们的类型校验,也就是说,这个方案希望同时满足严格和弱类型校验两个阵营
-
API不会被强制适应某个类型声明模式
-
因为文件默认使用弱类型校验方案,已经存在的代码库,可以在不破坏代码结构的情况下,添加标量类型声明。也可以让代码库逐步添加类型声明,或者仅部分模块添加
-
只需要一个单一语法,就可以定义标量类型声明
-
更喜欢严格类型校验的人,通常,不仅将这个特性使用在用户定义的函数,同时也使用在拓展和PHP内置函数中。也就是说,PHP使用者会得到一个统一机制,而不会产生严格标量声明的矛盾
-
在严格类型校验模式下,拓展和PHP内置函数产生的类型校验失败的错误级别,和用户自定函数产生的会保持一致,都是
E_RECOVERABLE_ERROR
-
它允许严格类型和弱类型代码,在一个单一的代码库中无缝集成
————
最后,欢迎大家关注我哦。

网友评论