惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News | PayPal Newsroom
Application and Cybersecurity Blog
Application and Cybersecurity Blog
O
OpenAI News
S
Security @ Cisco Blogs
宝玉的分享
宝玉的分享
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
Google Online Security Blog
Google Online Security Blog
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
TaoSecurity Blog
TaoSecurity Blog
Help Net Security
Help Net Security
Latest news
Latest news
NISL@THU
NISL@THU
S
Security Affairs
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
AI
AI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Announcements
Recent Announcements
P
Privacy & Cybersecurity Law Blog
小众软件
小众软件
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
AWS News Blog
AWS News Blog
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
I
InfoQ
Schneier on Security
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
IT之家
IT之家
T
Threatpost
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
N
News and Events Feed by Topic
L
LINUX DO - 最新话题
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
H
Heimdal Security Blog
S
SegmentFault 最新的问题

文章列表 - 她和她的猫

使用小鹤双拼一个月 折腾 Hugo PaperMod 主题 去长鹿旅游休博园 1Password 自动化登录堡垒机 深入 Hyperf:Inject 注解是如何工作的? 译|理解容器镜像层 使用 Rust 实现高性能预写日志(Write Ahead Log) 译|使用 Linux 命名空间隔离系统 深入 Hyperf:HTTP 服务是如何处理请求的? Linux 文件权限学习笔记 译|了解 Bitcask:基于日志结构的 KV 存储引擎 还原 SM2 压缩公钥的几种方法 深入 Hyperf:HTTP 服务启动时发生了什么? 一次 Hyperf 注解失效问题分析 将博客迁移到又拍云 译|PHP 7 新的 Hashtable 实现 排查 ES 查询问题:深入了解 json_encode() 函数 浅析 Redlock 分布式锁实现原理 Yar 源码阅读笔记:RPC 服务端 Yar 源码阅读笔记:客户端的并行调用 Yar 源码阅读笔记:客户端的同步调用 Yar 源码阅读笔记:数据传输模块 Yar 源码阅读笔记:消息编码模块 Yar 源码阅读笔记:RPC 通信协议 Yar 源码阅读笔记:开篇 基于 OpenSSL 实现国密 SM4 加解密 PHP 源码阅读笔记:编译与调试 PHP 什么是惊群问题 大端模式和小端模式 基于 GitHub Actions 定时推送网址到百度站长平台 聊聊五种 I/O 模型 Workerman 源码分析:文件上传 浅入浅出 HTTP 协议 PHP 网络编程:构建 MySQL 蜜罐获取攻击者微信 ID 使用 Workerman 接入 Bilibili 直播弹幕协议 使用 GitHub Actions 自动部署 Hexo 什么是二进制安全 【转载】PHP 程序员进阶之路 优化 Workerman 检查主进程是否存活的逻辑 基于 Redis 实现分布式锁 《PHP 实现 Base64 编码/解码》笔记 PHP 多进程下载必应壁纸 基于 Redis 实现延迟队列 【转载】PHP 实现 Base64 编码/解码 使用 MySQL FIELD() 函数自定义排序规则 Laravel 的 Facades 实现原理 C语言单链表实现约瑟夫环 数据结构学习笔记:顺序栈和链栈(C语言) 使用 QueryList + Redis 下载壁纸 在 Laravel 登录/注册中使用 mews/captcha 扩展包 使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码 给 YOURLS 短网址系统编写插件《批量生成短网址》 给 YOURLS 短网址系统编写插件《Hello World!》 PHP 7.1 使用 json_encode 函数造成浮点类型数据出现精度问题 Laravel 授权策略(Policy)的基本使用 Ubuntu 系统下 WiFi 频繁掉线解决方法 Laravel 应用部署到 Nginx 服务器上的第一个坑 Ubuntu 16.04 LST 安装 Redis 和 Composer PHP 采集逐浪小说章节列表 Nginx 环境 SSL 强制 HTTP 301 跳转到 HTTPS 【工作&生活】第一周 ServiceStack.Redis 与多线程 Count the number of Duplicates C# Base64 加密及解密 ASP.NET MVC 使用动态类型传递数据 Django 使用 order_by() 对数据进行排序操作 ASP.NET + jQuery + AJAX 实现用户登录 ASP.NET 从客户端中检测到有潜在危险的 Request.Form 值 ASP.NET 使用 FormData 实现 AJAX 上传图片 Python 使用 BeautifulSoup 抓取网页 贪心算法之字符串的完美度 一言不和就造轮子之 CookieHelper ASP.NET 上传图片 DBHelper 类 C# 捕获屏幕源码 用 JavaScript 做一些有趣的事情 第一篇文章 好久不见。 如何防止非法调用 PHP 文件 清明小记。 PHP 生成随机字符串 Z-Blog PHP 程序做 301 跳转 PHP 处理数组常用的几个函数 Sublime Text 3 快捷键总结 PHP 使用 file_get_contents() 函数实现采集网页 使用 C# 编写学生信息管理系统 终于到了。 zblog报错: magic_quotes_gpc 和 safe_mode 世事无常。 如何使用 PHP 上传文件 安卓手机编写和运行 PHP 程序的软件 使用 AJAX 刷新、验证 PHP 图片验证码 PHP 生成图片验证码 PHP 之 session 的使用方法 PHP 之 cookie 的使用方法 PHP 实现分页原理详解 Thinkphp 框架中系统常量在 Js 文件中不解析的解决方法
PHP 8 新特性介绍
她和她的猫 · 2021-09-03 · via 文章列表 - 她和她的猫

前言

距离 PHP 8 发布已经有一年多了,这个版本是 PHP 语言的主版本更新,包含了很多新功能与优化项,并改进了类型系统、错误处理,目前已经迭代到 PHP 8.0.10 版本。

由于更新的内容较多,本文仅介绍部分特性,完整内容可以去官网进行了解。

注解

注解是非常强大的一项功能,可以通过注解的方式减少很多配置,以及实现很多非常方便的功能,比如定义路由、AOP、自动注入等。

大多数框架都是通过反射解析 PHPDoc 的方式实现注解功能,比如我们现在用的 Hyperf 框架。

/**
 * @Controller()
 */
class UserController
{
    /**
     * @RequestMapping(path="index", methods="get,post")
     */
    public function index(RequestInterface $request)
    {
        ...
    }
}

PHP 社区显然也发现了注解对于现代编程语言的重要性,所以在 PHP 8 中从语言层面实现了注解,使得代码中的声明部分都可以添加结构化、机器可读的元数据,而非 PHPDoc 声明。

#[Controller]
class UserController
{
    #[RequestMapping(path: "index", methods: "get,post")]
    public function index(RequestInterface $request)
    {
        ...
    }
}

可以看到,这里使用的是 #[],为什么没有继续沿用 PHPDoc 中 @ 的方式?

因为 @ 在 PHP 中已经是一个有意义(抑制错误)的符号,不能再用来作为注解标识。

一开始是使用 <<ExampleAttribute>> 作为注解,由于吐槽的人太多,后来就改成了 #[ExampleAttribute]

命名参数

在 PHP 8 之前,调用函数时参数的顺序是固定的,并且不能跳过默认值进行传参。

  • 如果不按照函数声明的参数顺序进行传参,那么在函数中接收到的参数值也是不正确的,假如声明了参数类型,还会提示参数类型错误。
  • 当函数参数中有参数存在默认值,我们想要修改该参数后面的参数时,则必须要传入默认值才能达到目的,不能省略掉默认值。

命名参数允许直接以任意顺序使用参数名称进行传参,并且可以跳过某些默认值。

举个例子,htmlspecialchars 函数的原型如下:

htmlspecialchars(
    string $string,
    int $flags = ENT_COMPAT | ENT_HTML401,
    string $encoding = ini_get("default_charset"),
    bool $double_encode = true
): string

在 PHP 7 中使用该函数时,假如我们只想让 $double_encode 为 false,则必须传入 $flags 和 $encoding 默认值,像下面这样:

$val = '<b>hello</b>';

htmlspecialchars($val, ENT_COMPAT | ENT_HTML401, ini_get("default_charset"), false);

有了命名参数之后,我们可以忽略 $flags 和 $encoding 参数,仅传入 $string 和 $double_encode,并且可以随意调换参数顺序,下面这几种使用方式都是可以的。

$val = '<b>hello</b>';

// 忽略 $flags 和 $encoding 参数
htmlspecialchars($val, double_encode: false);
// 调换入参顺序
htmlspecialchars(double_encode: false, string: $val);
// 覆盖 $flags 的默认值
htmlspecialchars(double_encode: false, string: $val, flags: ENT_COMPAT);

联合类型

PHP 7.4 版本增加了类型属性,强化了 PHP 的类型系统,使得我们可以在类中声明属性的类型。

class Number 
{
    private int $number;
    
    public function __construct(int $number) 
    {
        $this->number = $number;
    }
}

但是该版本不支持联合类型,比如当 $number 既是 int 类型又是 float 类型时,就不能使用类型属性,只能通过 PHPDoc 注释的方式声明。

class Number 
{
    /**
     * @var float|int 
     */
    private $number;
    
    public function __construct($number) 
    {
        $this->number = $number;
    }
}

这样就又回到了以前弱类型的老路子,失去了类型属性的作用。

所以 PHP 8 引入了联合类型,使得我们可以在类属性及参数签名中声明多种类型信息。

class Number 
{
    private float|int $number;
    
    public function __construct(float|int $number) 
    {
        $this->number = $number;
    }
    
    public function getNumber(): int|float 
    {
        return $this->number;
    }
}

联合类型带来的好处:

  • 类型是强制要求的,可以及早发现错误。
  • 因为是强制要求的,所以类型信息不太可能会出现遗漏的情况。
  • 可以通过反射获取类型信息。
  • 语法比起 PHPDoc 注释简洁很多。

更符合逻辑的比较

在旧版本的 PHP 中,如果用数字跟字符串进行非严格比较,会得到一些令人困惑的结果:

var_dump(0 == ""); // true
var_dump(0 == "foo"); // true
var_dump(42 == "42foo"); // true
var_dump(in_array(0, ["foo", "bar"])); // true

导致这一结果是因为:非严格比较运算符的字符串和数字之间的比较,是将字符串转为数字,然后对整数或浮点数进行比较。

在上面的示例中,"""foo" 会被转换为 0,"42foo" 会被转换为 42,然后在跟左侧的值进行比较,所以结果为 true。

PHP 8 比较数字字符串时,会按数字进行比较。不是数字字符串时,将数字转化为字符串,按字符串比较。

比如 42 == "42",会直接按照数字进行比较,如果是 42 == "42foo",由于 "42foo" 不是数字字符串,所以会将 42 转为 "42" 字符串,然后再进行比较。

所以在 PHP 8 中,上面示例的结果全部是 false。

统一内部函数类型错误

在调用 PHP 函数时,如果传入的参数解析失败或者缺少参数,PHP 将会提示 Warning 并继续运行:

strlen([]); // Warning: strlen() expects parameter 1 to be string, array given
array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0
array_filter();

echo "hello";

//Warning: strlen() expects parameter 1 to be string, array given in /in/K0adM on line 2
//Warning: array_chunk(): Size parameter expected to be greater than 0 in /in/K0adM on line 3
//Warning: array_filter() expects at least 1 parameter, 0 given in /in/K0adM on line 4
//hello

有些“机智”的朋友会使用 error_reporting(E_ALL ^ E_WARNING) 来屏蔽异常,让程序能够继续正常运行。

就导致了函数虽然“正常”地返回了结果,但是结果不一定是正确的,所以我们就必须在代码里面对函数的结果进行校验。

PHP 8 针对这个问题进行了改进,统一了内部函数类型错误,现在大多数内部函数在参数验证失败时抛出 Error 级异常。

Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, array given in /in/F26cS:3
Stack trace:
#0 {main}
  thrown in /in/F26cS on line 3

Process exited with code 255.

由此,我们也可以看出 PHP 社区的目的,避免通过返回值判断异常情况,而是使用抛出异常的方式。

即时编译

PHP 8 引入了两个即时编译引擎,Tracing JIT 和 Function JIT,前者更具有潜力,它在综合基准测试中显示了三倍的性能, 并在某些长时间运行的程序中显示了 1.5-2 倍的性能改进。

PHP 8 的 JIT 是在 Opcache 优化的基础之上,结合 Runtime 的信息再次优化,直接生成机器码。

下面是 Opcache 的流程示意图:

最后

推荐一个网站:https://3v4l.org/,支持300多个版本的PHP在线运行代码,可以很方便地在线调试各个版本的差异。

参考阅读