php7.4分享

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地址

代码兼容

其他

你可能感兴趣的:(php7.4分享)