2015年12月3日,PHP 开发小组宣布第一个可用的PHP 7.0版本诞生了!这一天,标志着PHP版本进入了7系列:
http://php.net/archive/2015.php#id2015-12-03-1
PHP 7搭载了新版Zend引擎,做了大量的改善和提升,并提供了很多新特性,比如:
1. 速度的提升,PHP 7的速度是PHP 5.6的两倍以上。
2. 内存使用显著优化。
3. 使用抽象语法树。
4. 支持64位,统一不同平台下的整型长度,字符串和文件上传都支持大于2GB。
5. 更多Error错误可以进行异常处理。
6. 更安全可靠的随机数生成。
7. 移除了旧的和不支持的 SAPIs 和扩展
8. 等等
对于PHP 7的新特性,由于改动很多,这里不一一做详细介绍,有兴趣可以直接去官网访问:
http://php.net/manual/en/migration70.new-features.php
下面是在使用中,觉得很有帮助的新特性,我在此作汇总。
4种标量类型声明:int,float,string,bool
2种模式:强制 (默认) 和 严格模式
默认情况下,所有的PHP文件都处于弱类型校验模式。新的declare指令,通过指定strict_types的值(1或者0),1表示严格类型校验模式,作用于函数调用和返回语句;0表示弱类型校验模式。declare(strict_types=1)必须是文件的第一个语句。如果这个语句出现在文件的其他地方,将会产生一个编译错误,块模式是被明确禁止的。
strict_types指令只影响指定使用的文件,不会影响被它包含(通过include等方式)进来的其他文件。
举例,在默认情况下(strict_types=0):
function foo(int $val)
{
echo $val;
}
foo(0); //输出0
foo('1'); //输出1
foo('test'); //报错
此时,最后一个会报错,提示类型不对:
Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type integer, string given.
我们修改参数,在严格情况下(strict_types=1):
function foo(int $val)
{
echo $val;
}
foo(0); //输出0
foo('1'); //报错
foo('test'); //报错
我们发现,最后两个都会报错,提示类型不对:
Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type integer, string given.
也就是说,默认情况下,会将字符串1转换为int 1,而严格模式下,则不会转换。
在使用过程中,哪个文件需要严格模式,就在哪个文件头部声明:
strict_types指令影响同一个文件下的所有函数调用,不管这个被调函数是否在这个文件内定义的,都会采用严格类型校验模式。
在现代编程语言的实际应用中,有三种主要的方法去检查参数和返回值的类型:
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 7默认采用弱类型校验机制,同时追加一个开关,允许转换为广泛类型校验机制(也就是严格类型校验机制)。
为什么要在每个文件里各自声明strict_types校验模式,而不是统一声明,或者直接在函数声明。其实有很多好处:
1. 人们可以选择适合他们的类型校验,也就是说,这个方案希望同时满足严格和弱类型校验两个阵营。
2. API不会被强制适应某个类型声明模式。
3. 因为文件默认使用弱类型校验方案,已经存在的代码库,可以在不破坏代码结构的情况下,添加标量类型声明。也可以让代码库逐步添加类型声明,或者仅部分模块添加。
4. 只需要一个单一语法,就可以定义标量类型声明。
5. 更喜欢严格类型校验的人,通常,不仅将这个特性使用在用户定义的函数,同时也使用在拓展和PHP内置函数中。也就是说,PHP使用者会得到一个统一机制,而不会产生严格标量声明的矛盾。
6. 在严格类型校验模式下,拓展和PHP内置函数产生的类型校验失败的错误级别,和用户自定函数产生的会保持一致,都是E_RECOVERABLE_ERROR。
7. 它允许严格类型和弱类型代码,在一个单一的代码库中无缝集成。
具体参考:
https://wiki.php.net/rfc/scalar_type_hints_v5
函数的返回值支持类型声明,和标量类型声明类似,也会根据strict_types而采用相应的模式。
比如,这个代码会返回3:
如果改为严格模式:
报错:Uncaught TypeError: Return value of getNum() must be of the type integer, float returned
由于日常使用中存在大量同时使用三元表达式和 isset()的情况, 我们添加了null合并运算符 (??) 这个语法糖。如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数。
语法糖是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。
我们在开发web应用时,经常会读取GET和POST参数,为了更好的兼容,有些参数如果不传则使用默认值,代码可能如下:
在PHP 7中,我们可以使用一种更为简洁的方式:
$username = $_GET['user'] ?? 'nobody';
更一般的,可以对GET和POST都做兼容处理:
对于三元表达式,这里强调一点,三元表达式和null合并运算符不一样,如果变量是0,两者还是有区别的:
null合并运算符,仅判断是否存在,是否为null,因此0或者false都被视为存在。
Spaceship operator,也叫太空船操作符,主要是长得像一艘船:
<=>
太空船操作符用于比较两个表达式,当$a小于、等于或大于$b时它分别返回-1、0或1。
很好理解,直接看官方代码:
// 整数
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
// 浮点数
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
// 字符串
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
对于这一操作符,我问过几个人,他们都知道这个符号是什么,做什么,但是问他们应用场景的时候,一般都不知道,或者说可能用于排序,也不清楚什么样的排序场景会用到。
其实,太空船主要是和php排序方法结合使用,可以在一些场景下更简洁优雅,比如usort、uasort、uksort等:
[
'foo' => 5.5,
'bar' => 'abc'
],
[
'foo' => 7.7,
'bar' => 'xyz'
],
[
'foo' => 2.2,
'bar' => 'efg'
]
];
// Sort $things by 'foo' property, ascending
usort($things, function ($a, $b) {
return $a['foo'] <=> $b['foo'];
});
print_r($things);
// Sort $things by 'bar' property, descending
usort($things, function ($a, $b) {
return $b['bar'] <=> $a['bar'];
});
print_r($things);
代码会分别以升序和降序进行排序,输出如下:
(
[0] => Array
(
[foo] => 2.2
[bar] => efg
)
[1] => Array
(
[foo] => 5.5
[bar] => abc
)
[2] => Array
(
[foo] => 7.7
[bar] => xyz
)
)
Array
(
[0] => Array
(
[foo] => 7.7
[bar] => xyz
)
[1] => Array
(
[foo] => 2.2
[bar] => efg
)
[2] => Array
(
[foo] => 5.5
[bar] => abc
)
)
对于随机数的生成,PHP 7加入了高安全级别的随机字符串和随机整数两个函数:
string random_bytes ( int $length )
int random_int ( int $min , int $max )
这两个函数,会借助系统内置的高安全级别随机:
Windows系统使用CryptGenRandom。
Linux系统使用getrandom(2) syscall。
在其他平台会使用/dev/urandom。
如果以上平台找不到对应的内置方法,则进行报错处理。
对于加密要求不高的随机数,random_int和rand没什么区别,都是随即返回指定区间的随机数,stackoverflow上面有讨论,可以参考:
PHP rand() vs. random_int()
代码效果如下:
assert() 方法是向后兼用并增强之前的 assert() 的方法,它使得在生产环境中启用断言为零成本,并且提供当断言失败时抛出特定异常的能力。老版本的API出于兼容目的将继续被维护,assert()现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的string或一个待测试的boolean。
在PHP 7中,assert()是一种语言结构,允许对期望进行定义:在开发和测试环境中生效的断言,但在生产过程中被优化为零成本。
为了向后兼容,虽然assert_options()仍然可以用于行为控制,但是PHP 7提供了两个新的配置指令来控制其行为,而不是调用assert_options方法。
指令 | 默认值 | 描述 |
zend.assertions | 1 |
1: 生成并执行代码 (开发环境) 0: 生成代码但跳过执行 -1: 不生成代码 (生产环境) |
assert.exception | 0 |
1: 当断言失败时抛出指定的异常,在没有提供异常的情况下抛出一个新的AssertionError对象 0: 不会触发异常,仅发出警告(和PHP 5兼容) |
1. 安装php时,php.ini-development和php.ini-production文件中,zend.assertions的值分别是1和-1。
2. 关于assert的配置指令,直接在php.ini文件修改,ini_set不生效。
官方提供的代码示例,比较清晰解释了指令的具体作用,如下。
zend.assertions=0时,输出如下:
zend.assertions=1,assert.exception=0时,输出如下:
zend.assertions=1,assert.exception=1时,输出如下:
Stack trace:
#0 -(2): assert(false, 'assert(true == ...')
#1 {main}
thrown in - on line 2
针对PHP语法编写,进行了一些优化,让我们的代码更加美观。
通过 define() 定义常量数组:
匿名类的支持:
use的一次性导入:
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;
use function some\namespace\fn_a;
use function some\namespace\fn_b;
use function some\namespace\fn_c;
use const some\namespace\ConstA;
use const some\namespace\ConstB;
use const some\namespace\ConstC;
// PHP 7+ 及更高版本的代码
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
session_start() 可以接受一个 array 作为参数, 用来覆盖 php.ini 文件中设置的 会话配置选项:
最后,附上PHP 7性能对比图:
pupil
shy总准备给cc升php7了嘛
ranshy
明年应该会升级一批吧,有的项目出于稳定性考虑,可能暂时不动吧~