php7.3, php7.4相关介绍
[toc]
mac本地 php7.4源码安装
- 可能提前需要安装 re2c, bison
brew install bison
brew install re2c
- FFI 默认关闭,需要主动开启
YACC=/usr/local/opt/[email protected]/bin/bison ./configure --with-ffi --enable-opcache [option...]
docker安装
wget -O dnmp.zip https://pan.yidian-inc.com/index.php/s/iU0MjWdpN3UKG0V
unzip dnmp.zip && cd dnmp
docker-composer up -d
新特性
更加灵活的heredoc和nowdoc
- 支持闭合标记符的缩进
- 不再强制闭合标记符的换行;
$libc = FFI::cdef(
<<argInt(33));
类属性支持类型定义,并严格限制其使用
当给一个int型的属性赋值一个string类型,会引发fatal error
# Fatal error: Uncaught TypeError: Typed property User::$id must be int, string used
# TypeError: Typed property User::$id must be int, string used
class User {
public int $id;
public string $name;
}
$user = new User();
$user->id = 'test';
支持调用时参数的尾随逗号
只是在调用函数时可用,函数定义是不允许的。
提高了代码的可读性, 在需要频繁加参数的场景中,可以减少忘记加逗号而引起的错误。
也便于调试
var_dump(
$result,
$dbData,
);
json_decode新增错误标识JSON_THROW_ON_ERROR
当json解析错误时,会抛异常JsonException。
而在php7.2中,解析失败会返回null,需要再通过json_last_error来判断是否解析错误。
try {
$json = json_doecode('{', false, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $exception) {
echo $exception->getMessage();
}
null合并运算符
$var ??= 'test string';
// 等同于
if(!isset($var)) {
$var = 'test string';
}
支持数组内解
Spread 运算符应该比 array_merge 拥有更好的性能。
这不仅仅是 Spread 运算符是一个语法结构,而 array_merge 是一个方法。还是在编译时,优化了高效率的常量数组
新函数 array_key_first,array_key_last
// 新函数 array_key_first 返回数组的第一个key
$firstKey = array_key_first($array);
// ==等同于
reset($array); // 重置内部指针到第一个位置
$firstKey = key($array); // key指向当前内部指针所指向的位置
// 新函数 array_key_last 返回数组的最后一个key
$endKey = array_key_last($array);
// ==等同于
end($array); // 重置内部指针到最后一个位置
$endKey = key($array);
其他参考
https://www.php.net/manual/en/migration73.new-features.php
https://www.php.net/manual/en/migration74.new-features.php
向后不兼容修改
heredoc和nowdoc的结束符不能在内部有定义
# 在php7.2可正常执行并输出 test\r\nEOF, 在php7.3及其以后,会抛异常
# Parse error: Invalid body indentation level (expecting an indentation level of at least 4)
$test = <<
数组方式访问null,bool,int,float,res等类型($null['key'])会引发一个notice
Notice: Trying to access array offset on value of type null
其他参考:
https://www.php.net/manual/en/migration73.incompatible.php
https://www.php.net/manual/en/migration74.incompatible.php
废弃功能
大小写不敏感的常量
将 TRUE 作为第三个参数传递给 define() 将会导致一个废弃警告
Deprecated: define(): Declaration of case-insensitive constants is deprecated
array_key_exists()用于对象中已被废弃
应该使用isset() or property_exists()
Deprecated: array_key_exists(): Using array_key_exists() on objects is deprecated. Use isset() or property_exists() instead
大括号获取数组元素已被弃用
Use arr['idx'] instead of arr{'idx'}.
Deprecated: Array and string offset access syntax with curly braces is deprecated
其他参考:
https://www.php.net/manual/en/migration73.deprecated.php
https://www.php.net/manual/en/migration74.deprecated.php
opcache.preload 新特性介绍
opcode&opcache基础介绍
graph LR;
G[request] -.-> A[.php] -.-> B[词法分析] -.-> C[语法分析] -.-> D[生成opcode] -.-> E[执行opcode] -.-> F[response]
每一次请求PHP脚本都会执行一遍以上步骤,如果PHP源代码没有变化,那么Opcode也不会变化,显然没有必要每次都重新生成Opcode
由此,我们可以缓存opcode,缓存后的执行流程如下:
graph LR;
A[request] -.-> B[.php] -.-> H{获取opcache}
H -.-> |yes|C[执行opcode] -.-> G[response]
H -.-> |no|D[词法分析] -.-> E[语法分析] -.-> F[生成opcode] -.-> C[执行opcode]
F -.-> I[生成opcache]
opcache的目的就是避免重复编译,减少CPU和内存的消耗
opcache的不足
- 从缓存中获取文件的成本。
- 检查源文件是否被修改
- 单个文件缓存,无法得知不同文件类的依赖关系,仍然需要重新连接类依赖项。
preload原理
官方文档指出, 该项提案受 类共享内存的启发(“Class Data Sharing” technology designed for Java HotSpot VM. )
在运行任何应用程序代码之前 - 我们可以将一组PHP文件加载到内存中 - 并使其内容“permanently available”到该服务器将服务的所有后续请求。这些文件中定义的所有函数和类将可用于开箱即用的请求,与内部实体完全相同(例如strlen()方法或Exception类)。
preload使用限制
- class, funciton, trait, interface能够进行预加载(在满足依赖的情况下)
- 全局变量,define, const,无法被预加载
- 类变量定义未使用的会有preload warning警告
- 预加载只加载文件,不执行文件,因此动态生成的一切无法被预加载。
- 一个类具有未预加载的依赖项,则其本身也不能被预加载
由于不同的应用程序(或同一应用程序的不同版本)在不同的文件中可能有相同的类/函数名,如果类的一个版本是预加载的——它将防止加载在不同文件中定义的该类的任何其他版本。
preload使用
修改php.ini配置文件:
# 在服务器启动的时候,就会去加载该路径的文件,进行预加载
opcache.preload=/var/www/html/website/vendor/preload.php
# php-fpm指定的user
opcache.preload_user=www-data
cmposer预加载支持
https://github.com/composer/composer/issues/7777
composer g require ayesh/composer-preload
cd website/
# edit composer.json
mkdir -p webroot/vendor
composer preload
php-fpm restart
composer.json
{
"extra": {
"preload": {
"paths": [
"base",
"dao",
"util",
"cron",
"yaconf"
],
"exclude": [
"vendor"
],
"extensions": ["php", "ini"],
"no-status-check": false
}
}
}
更多细节:https://github.com/Ayesh/Composer-Preload
通过 var_dump(opcache_get_status()['scripts'])
可以查看预加载的文件
Composer-preload 注意点
- 初始化预加载后,如果有在预加载文件之类的一些请求,也会继续被预加载。
- 代码存在已被废弃的功能,生成预加载文件后会导致fpm无法启动。
使用性能总结
- 预加载可以提升性能,但只有依赖繁多时才会起到明显效果。
- 即使只开启opcache,性能提供也非常明显。升级php7.4得到的性能提供明显。
- 其他:https://github.com/composer/composer/issues/7777#issuecomment-440268416
FFI 新特性介绍
FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI。libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。
项目使用
扩展部分
- yaconf 官方github未提供php74的版本, 有第三方版本可以使用。第三方github
- protobuf 官方github未提供php74的版本, 第三方版本编译通过但仍会报错。ISSUES地址