不久前,PHP 8.0大张旗鼓地发布了。它带来了许多新特性、性能增强和变化——其中最令人兴奋的是新的JIT编译器。
技术世界总是在向前发展,PHP也是如此。
PHP 8.1也快来了,包含了几个令人兴奋的特性。它定于今年晚些时候于2021年11月25日发布。
在本文中,我们将详细介绍PHP 8.1将带来哪些新的东西。从其新特性和性能改进到重大更改和弃用,我们将深入介绍它们。
PHP 8.1中的新特性
让我们首先介绍PHP 8.1中的所有新特性。这是一个相当多的清单。
注意:随着PHP 8.1发布日期的临近,此列表可能会增加或缩小。我们将致力于使其保持最新状态。
- 纯交集类型
- 枚举
- 永不返回类型
- Fibers
- 新的只读属性
- 定义最终类常量
- 新的fsync()和fdatasync()函数
- 新的array_is_list()函数
- 新的Sodium XChaCha20函数
- 新的IntlDatePatternGenerator类
- 支持AVIF图像格式
- 新的$_FILES:目录上传的full_path键
- 对字符串键控数组的数组解包支持
- 显式八进制数字表示法
- MurmurHash3和xxHash哈希算法支持
- DNS-over-HTTPS (DoH) 支持
- 使用CURLStringFile从字符串上传文件
- 新的MYSQLI_REFRESH_REPLICA常量
- 使用继承缓存提高性能
- 一流的可调用语法
纯交集类型
PHP 8.1添加了对交集类型的支持。它类似于PHP 8.0 中引入的联合类型,但它们的预期用途恰恰相反。
为了更好地理解它的用法,让我们回顾一下PHP中类型声明的工作原理。
本质上,您可以向函数参数、返回值和类属性添加类型声明。这种分配称为类型提示,并确保值在调用时是正确的类型。否则,它会立即抛出一个TypeError。反过来,这可以帮助您更好地调试代码。
但是,声明单一类型有其局限性。联合类型通过允许您声明具有多种类型的值来帮助您克服这个问题,并且输入必须至少满足声明的类型之一。
另一方面,RFC将交集类型描述为:
“交集类型”需要一个值来满足多个类型约束而不是单个约束。
…纯交集类型使用语法 T1&T2&… 指定,可用于当前接受类型的所有位置…
请注意使用&
(AND) 运算符来声明交集类型。相反,我们使用|
(OR) 运算符来声明联合类型。
在交集类型中使用大多数标准类型将导致永远无法满足的类型(例如整数和字符串)。因此,交集类型只能包括类类型(即接口和类名)。
以下是如何使用交集类型的示例代码:
class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }
在上面的代码中,我们将变量countableIterator定义为两种类型的交集:Traversable和Countable。在这种情况下,声明的两种类型是接口。
交集类型也符合已用于类型检查和继承的标准PHP变化规则。但是还有两个关于交集类型如何与子类型交互的额外规则。您可以在其 RFC 中阅读有关交集类型差异规则的更多信息。
在某些编程语言中,您可以在同一个声明中组合联合类型和交集类型。但是PHP 8.1禁止它。因此,它的实现被称为“纯”交集类型。但是,RFC 确实提到它“留作未来范围”。
枚举
PHP 8.1终于添加了对枚举(也称为枚举或枚举类型)的支持。它们是用户定义的数据类型,由一组可能的值组成。
编程语言中最常见的枚举示例是布尔类型,具有true
和false
两个可能的值。它是如此普遍,以至于它融入了许多现代编程语言。
根据RFC,PHP 中的枚举首先将被限制为“单元枚举”:
此RFC的范围仅限于“单元枚举”,即枚举本身就是一个值,而不是简单的原始常量的花哨语法,并且不包括附加的相关信息。此功能极大地扩展了对数据建模、自定义类型定义和 monad 样式行为的支持。枚举启用了“使无效状态不可表示”的建模技术,这会导致更健壮的代码,而无需进行详尽的测试。
为了达到这个阶段,PHP团队研究了许多已经支持枚举的语言。他们的调查发现,您可以将枚举分为三类:花式常量、花式对象和完整代数数据类型 (ADT)。这是一个有趣的阅读!
PHP 实现了“Fancy Objects”枚举,并计划在未来将其扩展到完整的ADT。它在概念和语义上都模仿了Swift、Rust和Kotlin中的枚举类型,尽管它没有直接模仿它们中的任何一个。
RFC使用一副牌中著名的西装类比来解释它是如何工作的:
enum Suit { case Hearts; case Diamonds; case Clubs; case Spades; }
在这里,Suit枚举定义了四个可能的值:Hearts、Diamonds、Clubs和Spades。您可以直接访问使用语法这些值:Suit::Hearts
,Suit::Diamonds
,Suit::Clubs
,和Suit::Spades
。
这种用法可能看起来很熟悉,因为枚举是建立在类和对象之上的。它们的行为相似并且具有几乎完全相同的要求。枚举与类、接口和特征共享相同的命名空间。
上面提到的枚举称为Pure Enums。
如果您想为任何情况提供标量等效值,您还可以定义支持枚举。但是,支持的枚举只能有一种类型,int
或者string
(永远不会)。
enum Suit: string { case Hearts = 'H'; case Diamonds = 'D'; case Clubs = 'C'; case Spades = 'S'; }
此外,支持枚举的所有不同情况都必须具有唯一值。你永远不能混合纯枚举和支持枚举。
RFC进一步深入探讨了枚举方法、静态方法、常量、常量表达式等等。涵盖所有这些超出了本文的范围。您可以参考文档以熟悉它的所有优点。
never
返回类型
PHP 8.1添加了一个名为never
. 在 alwaysthrow
或exit
.
根据它的RFC,URL重定向函数总是exit
(显式或隐式)是其使用的一个很好的例子:
function redirect(string $uri): never { header('Location: ' . $uri); exit(); } function redirectToLoginPage(): never { redirect('/login'); }
一个never
-declared函数应满足三个条件:
- 它不应该有
return
明确定义的语句。 - 它不应该有
return
隐式定义的语句(例如if-else语句)。 - 它必须以
exit
语句(显式或隐式)结束其执行。
上面的URL重定向示例显示了never
返回类型的显式和隐式用法。
在never
返回类型共享与许多相似之处void
返回类型。它们都确保函数或方法不返回值。但是,它的不同之处在于执行更严格的规则。例如,void
-declared 函数仍然可以return
没有显式值,但您不能对never
-declared 函数执行相同的操作。
根据经验,void
当您希望 PHP 在函数调用后继续执行时,请继续执行。never
当你想要相反的时候就去吧。
此外,never
定义为“bottom”类型。因此,任何声明的类方法never
都“never”不能将其返回类型更改为其他类型。但是,您可以使用void
-declared 方法扩展never
-declared 方法。
info: 原始RFC将
never
返回类型列为noreturn
,这是两个PHP静态分析工具(即Psalm和PHPStan)已经支持的返回类型。由于这是由Psalm和PHPStan的作者自己提出的,因此他们保留了其术语。但是,由于命名约定,PHP团队对noreturn
vsnever
进行了民意调查,never
最终成为永远的赢家。因此,对于PHP 8.1+版本,始终使用never
代替noreturn
.
Fibers
从历史上看,PHP代码几乎一直是同步代码。代码执行暂停,直到返回结果,即使是 I/O 操作。您可以想象为什么这个过程可能会使代码执行速度变慢。
有多种第三方解决方案可以克服这一障碍,允许开发人员异步编写PHP代码,尤其是并发 I/O 操作。一些流行的示例包括amphp、ReactPHP和Guzzle。
但是,在PHP中没有处理此类实例的标准方法。此外,在同一个调用堆栈中处理同步和异步代码会导致其他问题。
Fibers是PHP通过虚拟线程(或绿色线程)处理并行性的方式。它试图通过允许 PHP 函数中断而不影响整个调用堆栈来消除同步和异步代码之间的差异。
以下是RFC的承诺:
- 向PHP添加对Fibers的支持。
- 引入一个新的Fiber类和对应的反射类ReflectionFiber。
- 添加异常类FiberError和FiberExit来表示错误。
- Fibers允许现有接口(PSR-7、Doctrine ORM等)的透明非阻塞I/O实现。那是因为占位符(promise)对象被消除了。相反,函数可以声明I/O结果类型,而不是无法指定解析类型的占位符对象,因为PHP不支持泛型。
您可以使用Fibers开发全栈、可中断的PHP函数,然后您可以使用这些函数在PHP中实现协作多任务处理。当Fiber暂停整个执行堆栈时,您可以放心,因为它不会损害您的其余代码。
图表说明了使用Fibers的PHP代码执行流程。
为了说明Fibers的用法,它的RFC使用了这个简单的例子:
$fiber = new Fiber(function (): void { $value = Fiber::suspend('fiber'); echo "Value used to resume fiber: ", $value, "\n"; }); $value = $fiber->start(); echo "Value from fiber suspending: ", $value, "\n"; $fiber->resume('test');
你在上面的代码中创建了一个“fiber”,并立即用字符串挂起它fiber
。该echo
声明用作fiber恢复的视觉提示。
您可以通过调用$fiber->start()
检索此字符串值。
然后,使用字符串“test”恢复fiber,该字符串是对Fiber::suspend()
的调用返回的。完整代码执行会产生如下输出:
Value from fiber suspending: fiber Value used to resume fiber: test
这是PHP Fibers工作的准系统教科书示例。这是执行七个异步GET请求的另一个Fibers示例。
尽管如此,大多数PHP开发人员永远不会直接处理Fibers。RFC甚至提出了同样的建议:
Fibers是大多数用户不会直接使用的高级功能。此功能主要针对库和框架作者,以提供事件循环和异步编程 API。Fibers允许在任何时候将异步代码执行无缝集成到同步代码中,而无需修改应用程序调用堆栈或添加样板代码。
Fiber API不应直接在应用程序级代码中使用。Fibers提供了一个基本的、低级别的流控制API来创建更高级别的抽象,然后在应用程序代码中使用这些抽象。
考虑到它的性能优势,您可以期待PHP库和框架能够利用这一新特性。看看他们如何在他们的生态系统中实施Fibers会很有趣。
新的readonly
属性
PHP 8.1添加了对readonly
属性的支持。它们只能从它们被声明的作用域初始化一次。初始化后,您永远无法修改它们的值。这样做会引发错误异常。
RFC写道:
一个只读属性只能使用一次,并且只能从已申报范围的初始化。对该属性的任何其他分配或修改都将导致Error异常。
这是一个如何使用它的示例:
class Test { public readonly string $kinsta; public function __construct(string $kinsta) { // Legal initialization. $this->kinsta = $kinsta; } }
一旦初始化,就再也回不去了。将此功能融入PHP会大大减少通常用于启用此函数的样板代码。
readonly
属性在类内部和外部都提供了强大的不变性保证。中间运行什么代码并不重要。调用readonly
属性将始终返回相同的值。
但是,readonly
在特定用例中使用该属性可能并不理想。例如,您只能将它们与类型化属性一起使用,因为没有类型的声明是隐式的null
,不能是readonly
。
此外,设置readonly
属性不会使对象不可变。该readonly
属性将保存相同的对象,但该对象本身可以更改。
此属性的另一个小问题是您无法克隆它。已经有针对此特定用例的解决方法。如有必要,请查看它。
定义final
类常量
从PHP 8.0开始,您可以使用其子类覆盖类常量。这是由于在PHP中实现继承的方式。
以下是如何覆盖先前声明的常量值的示例:
class Moo { public const M = "moo"; } class Meow extends Moo { public const M = "meow"; }
现在,如果奶牛想要更严格地控制猫的行为(至少在常量方面),他们可以使用PHP 8.1的新final
修饰符来实现。
一旦你声明了一个常量为 final
,就意味着。
class Moo { final public const M = "moo"; } class Meow extends Moo { public const M = "meow"; } // Fatal error: Meow::M cannot override final constant Moo::M
您可以在最后的类常量PHP RFC中阅读更多关于它的内容。
新的fsync()
和fdatasync()
函数
PHP 8.1添加了两个名为fsync()
和fdatasync()
新文件系统函数。对于那些习惯于Linux同名函数的人来说,它们似乎很熟悉。那是因为它们是相关的,只是为PHP实现的。
事实上,这一补充早该进行了。PHP是少数仍未实现fsync() 和 fdatasync() 的主要编程语言之一——也就是说,直到PHP 8.1。
fsync()
函数类似于PHP现有的fflush()
函数,但在一个方面有很大不同。而fflush()
将应用程序的内部缓冲区刷新到操作系统,fsync()
则更进一步并确保将内部缓冲区刷新到物理存储。这确保了完整且持久的写入,以便您即使在应用程序或系统崩溃后也可以检索数据。
这是一个如何使用它的示例。
$doc = 'kinsta.txt'; $kin = fopen($doc, 'ki'); fwrite($kin, 'doc info'); fwrite($kin, "\r\n"); fwrite($kin, 'more info'); fsync($kin); fclose($kin);
fsync()
在末尾添加调用可确保保存在 PHP 或操作系统内部缓冲区中的任何数据都被写入存储。在此之前,所有其他代码执行都将被阻止。
它的相关函数是fdatasync()
。使用它来同步数据,但不一定是元数据。对于元数据不是必需的数据,此函数调用会使写入过程更快一点。
但是,您应该注意PHP 8.1在Windows下尚不完全支持fdatasync()
。它只是作为fsync()
的别名。在POSIX上,fdatasync()
已正确实施。
新的array_is_list()
函数
PHP数组可以保存整数和字符串键。这意味着您可以将它们用于多种用途,包括列表、哈希表、字典、集合、堆栈、队列等等。您甚至可以在数组中包含数组,从而创建多维数组。
您可以有效地检查特定条目是否为数组,但检查它是否有任何缺失的数组偏移量、乱序键等就不是那么容易了。简而言之,您无法快速验证数组是否为列表。
所述array_is_list()函数检查是否一个数组的键是按顺序从0
开始,并与没有间隙。如果满足所有条件,它将返回true
。默认情况下,它还返回true
空数组。
以下是在同时满足true
和false
条件的情况下使用它的几个示例:
// true array_is_list() examples array_is_list([]); // true array_is_list([1, 2, 3]); // true array_is_list(['cats', 2, 3]); // true array_is_list(['cats', 'dogs']); // true array_is_list([0 => 'cats', 'dogs']); // true array_is_list([0 => 'cats', 1 => 'dogs']); // true // false array_is_list() examples array_is_list([1 => 'cats', 'dogs']); // as first key isn't 0 array_is_list([1 => 'cats', 0 => 'dogs']); // keys are out of order array_is_list([0 => 'cats', 'bark' => 'dogs']); // non-integer keys array_is_list([0 => 'cats', 2 => 'dogs']); // gap in between keys
带有乱序键的PHP数组列表是潜在的错误来源。在继续执行代码之前,使用此函数强制严格遵守列表要求是对PHP的一个很好的补充。
新的Sodium XChaCha20函数
Sodium是一个现代的、易于使用的加密库,用于加密、解密、密码散列、签名等。该PECL libsodium包增加了对钠的包装,使PHP开发人员可以使用它。
即使是Facebook、Discord、Malwarebytes和Valve等领先的科技公司也使用libsodium来通过快速安全的连接来保护他们的用户。
libsodium支持XChaCha20加密算法对数据进行加密和解密,特别是对流加密。同样,PECL libsodium扩展已经支持XChaCha20,但仅支持Poly1305消息验证代码。
许多PHP应用程序直接使用XChaCha20进行流加密。为了让事情变得更简单,从PHP 8.1开始,您将拥有三个新函数,无需身份验证即可使用XChaCha20加密或解密数据。这种模式称为“分离模式”。
新推出的XChaCha20函数是:
sodium_crypto_stream_xchacha20_keygen
: 返回一个安全的随机密钥,用于sodium_crypto_stream_xchacha20。sodium_crypto_stream_xchacha20
:将密钥和随机数扩展为伪随机字节的密钥流。sodium_crypto_stream_xchacha20_xor
:使用随机数和密钥(无身份验证)加密消息。
此外,在全局命名空间中定义了两个新的PHP常量:
SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES
(分配32)SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES
(分配24)
不过请谨慎使用。由于没有身份验证,解密操作容易受到常见的密文攻击。
您可以在GitHub页面上阅读有关其用法和要求的更多信息。
新的IntlDatePatternGenerator类
PHP的底层ICU库支持创建本地化的日期和时间格式,但不能完全自定义。
例如,如果您想在PHP 8.0之前创建特定于语言环境的数据和时间格式,您可以使用预定义的IntlDateFormatter常量以6种方式完成:
IntlDateFormatter::LONG
:更长,例如November 10, 2017 or 11:22:33pmIntlDateFormatter::MEDIUM
:短一点,比如November 10, 2017IntlDateFormatter::SHORT
:只是数字,比如10/11/17 or 11:22pm
其中每一个也有自己的RELATIVE_
变体,将日期格式设置在当前日期之前或之后的有限范围内。在 PHP 中,值是yesterday, today和tomorrow。
假设您想使用年份的长版本和月份的短版本,例如10/11/2017。但在PHP 8.0,你不能这样做。
在PHP 8.1+中,您可以使用新的IntlDatePatternGenerator类指定日期、月份和时间使用的格式。您可以将这些组件的确切顺序留给格式化程序。
您应该注意到,虽然这个类中只有Date一词,但它与ICU的DateTimePatternGenerator一致。这意味着您还可以使用它来创建灵活的时间格式。为了简化命名,PHP团队选择使用较短的IntlDatePatternGenerator术语。
这是直接来自其RFC的示例:
$skeleton = "YYYYMMdd"; $today = \DateTimeImmutable::createFromFormat('Y-m-d', '2021-04-24'); $dtpg = new \IntlDatePatternGenerator("de_DE"); $pattern = $dtpg->getBestPattern($skeleton); echo "de: ", \IntlDateFormatter::formatObject($today, $pattern, "de_DE"), "\n"; $dtpg = new \IntlDatePatternGenerator("en_US"); $pattern = $dtpg->getBestPattern($skeleton), "\n"; echo "en: ", \IntlDateFormatter::formatObject($today, $pattern, "en_US"), "\n"; /* de: 24.04.2021 en: 04/24/2021 */
在上面的代码中,skeleton变量定义了要使用的特定日期或时间格式。但是,格式化程序处理最终结果的顺序。
支持AVIF图像格式
AVIF或AV1图像文件格式,是一种基于AV1视频编码格式的相对较新的免版税图像格式。除了提供更高的压缩率(因此文件更小)之外,它还支持多种特性,例如透明度、HDR等。
AVIF格式最近才标准化(2021年6月8日)。这为Chrome 85+和Firefox 86+等浏览器增加了对AVIF图像的支持铺平了道路。
PHP 8.1的图像处理和GD扩展增加了对AVIF图像的支持。
但是,要包含此特性,您需要编译具有AVIF支持的GD扩展。您可以通过运行以下命令来执行此操作。
对于Debian/Ubuntu:
apt install libavif-dev
对于Fedora/RHEL:
dnf install libavif-devel
这将安装所有最新的依赖项。接下来,您可以通过--with-avif
使用./configure
脚本运行标志来编译AVIF支持。
./buildconf --force ./configure --enable-gd --with-avif
如果您要从头开始一个新环境,您还可以在此处启用其他PHP扩展。
安装后,您可以通过在PHP终端中运行以下命令来测试是否启用了AVIF支持:
php -i | grep AVIF
如果您已正确安装AVIF,您将看到以下结果:
AVIF Support => enabled
您还可以使用gd_info()
调用来检索GD功能列表,包括是否启用了AVIF支持功能。
这个更新的PHP 8.1 GD扩展还添加了两个用于处理AVIF图像的新函数:imagecreatefromavif
和imageavif
. 它们的工作方式与JPEG和PNG对应物类似。
imagecreatefromavif
函数从给定的AVIF图像返回一个GdImage实例。然后,您可以使用此实例来编辑或转换图像。
另一个imageavif
函数输出AVIF图像文件。例如,您可以使用它将JPEG转换为AVIF:
$image = imagecreatefromjpeg('image.jpeg'); imageavif($image, 'image.avif');
您可以在其GitHub页面上阅读有关此新特征的更多信息。
新的目录上传$_FILES: full_path
键
PHP维护了大量预定义变量来跟踪各种事物。其中之一是$_FILES 变量保存通过HTTP POST方法上传的项目的关联数组。
大多数现代浏览器都支持使用HTML文件上传字段上传整个目录。甚至PHP<8.1也支持此功能,但有一个很大的警告。您无法上传具有确切目录结构或相对路径的文件夹,因为PHP没有将此信息传递给$_FILES
数组。
这与另外一个名为新的关键的变化在PHP 8.1full_path
的$_FILES
阵列。使用这些新数据,您可以在服务器上存储相对路径或复制确切的目录结构。
您可以通过$FILES
使用var_dump($_FILES);
命令输出数组来测试此信息。
但是,如果您正在使用此功能,请谨慎操作。确保您防范标准文件上传攻击。
对字符串键控数组的数组解包支持
PHP 7.4添加了对使用数组展开运算符 ( … )进行数组解包的支持。它可以作为使用array_merge()
函数的更快替代方法。但是,此特性仅限于数字键数组,因为在合并具有重复键的数组时,解包字符串键数组会导致冲突。
但是,PHP 8添加了对命名参数的支持,消除了这个限制。因此,数组解包现在也支持使用相同语法的字符串键数组:
$array = [...$array1, ...$array2];
这个RFC示例说明了如何在PHP 8.1中处理合并具有重复字符串键的数组:
$array1 = ["a" => 1]; $array2 = ["a" => 2]; $array = ["a" => 0, ...$array1, ...$array2]; var_dump($array); // ["a" => 2]
在这里,字符串键“a”在通过数组解包合并之前出现了三次。但只有它属于$array2
的最后一个值获胜。
显式八进制数字表示法
PHP 支持各种数字系统,包括十进制 (base-10)、二进制 (base-2)、八进制 (base-8) 和十六进制 (base-16)。十进制数字系统是默认值。
如果你想使用任何其他数字系统,那么你必须在每个数字前加上一个标准前缀:
- 十六进制:
0x
前缀。(例如 17 =0x11
) - 二进制:
0b
前缀。(例如 3 =0b11
) - 八进制:
0
前缀。(例如 9 =011
)
您可以看到八进制数字系统的前缀与其他系统的前缀有何不同。为了标准化这个问题,许多编程语言增加了对显式八进制数字符号的支持:0o
或0O
。
从PHP 8.1开始,您可以在八进制数字系统中将上述示例(即以10为基数的数字9)编写为0o11
或0O11
。
0o16 === 14; // true 0o123 === 83; // true 0O16 === 14; // true 0O123 === 83; // true 016 === 0o16; // true 016 === 0O16; // true
此外,这个新特性也适用于PHP 7.4中引入的下划线数字文字分隔符。
在其RFC中阅读有关此新PHP 8.1特征的更多信息。
MurmurHash3和xxHash哈希算法支持
PHP 8.1添加了对MurmurHash3和xxHash散列算法的支持。它们不是为加密用途而设计的,但它们仍然提供令人印象深刻的输出随机性、分散性和唯一性。
这些新的散列算法比大多数PHP现有的散列算法都快。事实上,其中一些散列算法的变体比RAM吞吐量更快。
由于PHP 8.1还增加了对声明特定于算法的$options
参数的支持,您可以对这些新算法执行相同的操作。这个新参数的默认值为[]
。因此,它不会影响我们现有的任何哈希函数。
您可以在他们的GitHub页面上阅读有关这些PHP 8.1新功能的更多信息:MurmurHash3、xxHash、Algorithm-specific $options.
DNS-over-HTTPS (DoH) 支持
DNS-over-HTTPS (DoH) 是一种通过HTTPS协议进行DNS解析的协议。DoH使用HTTPS加密客户端和DNS解析器之间的数据,通过防止中间人攻击来提高用户隐私和安全性。
从PHP 8.1开始,您可以使用Curl扩展来指定DoH服务器。它需要使用libcurl 7.62+版本编译PHP。对于大多数流行的操作系统(包括Linux发行版)来说,这不是问题,因为它们通常包含Curl 7.68+。
您可以通过指定CURLOPT_DOH_URL
选项来配置DoH服务器URL 。
$doh = curl_init('https://www.wbolt.com'); curl_setopt($doh, CURLOPT_DOH_URL, 'https://dns.google/dns-query'); curl_exec($doh);
在上面的示例中,我们使用了Google的公共DNS服务器。另外,请注意https://
在所有使用的URL中的使用。确保完美地配置它,因为在Curl中没有可以回退的默认DNS服务器。
您还可以从Curl文档中包含的公共DoH服务器列表中进行选择。
此外,Curl文档的CURLOPT_DOH_URL 参考解释了如何彻底使用其各种参数。
使用CURLStringFile从字符串上载文件
PHP Curl扩展支持带有文件上传的HTTP(S)请求。它使用CURLFile类来实现这一点,该类接受URI或文件路径、mime类型和最终文件名。
但是,使用CURLFile类,您只能接受文件路径或URI,而不能接受文件本身的内容。如果您已经将文件上传到内存中(例如处理过的图像、XML文档、PDF),您必须使用data://
Base64编码的URI。
但是libcurl已经支持一种更简单的方式来接受文件的内容。新的CURLStringFile类增加了对此的支持。
您可以阅读其GitHub页面以了解有关如何在PHP 8.1中实现的更多信息。
新的MYSQLI_REFRESH_REPLICA
常数
PHP 8.1的mysqli扩展添加了一个名为MYSQLI_REFRESH_REPLICA
. 它相当于现有的MYSQLI_REFRESH_SLAVE
常数。
这个变化在MySQL 8.0.23中受到欢迎,以解决技术词汇中的种族不敏感问题(最常见的例子包括“奴隶”和“主人”)。
您应该注意到旧的常量没有被删除或弃用。开发人员和应用程序可以继续使用它。对于希望抛弃此类术语的开发人员和公司而言,此新常量只是一种选择。
使用继承缓存提高性能
继承缓存(Inheritance Cache )是opcache的新增功能,可消除PHP类继承开销。
PHP类由opcache单独编译和缓存。但是,它们已经在运行时针对每个请求进行了链接。这个过程可能涉及几个兼容性检查和从父类和特征借用方法/属性/常量。
因此,即使每个请求的结果都相同,这仍需要相当长的时间来执行。
继承缓存链接所有唯一的依赖类(父类、接口、特征、属性类型、方法)并将结果存储在opcache共享内存中。由于这种情况现在只发生一次,因此继承需要较少的指令。
此外,它消除了对不可变类的限制,例如未解析的常量、类型化属性和协变类型检查。因此,存储在opcache中的所有类都是不可变的,进一步减少了所需的指令数量。
总而言之,它有望带来显着的性能优势。该补丁的作者Dimitry Stogov发现它在基础Symfony “Hello, World!” 上有8%的改进。程序。我们迫不及待地想在我们的以下PHP基准测试中测试它。
First-Class可调用语法
PHP 8.1添加了First-Class可调用语法来取代使用字符串和数组的现有编码。除了创建更干净的Closure 之外,静态分析工具也可以访问这种新语法并尊重声明的范围。
以下是一些取自RFC的示例:
$fn = Closure::fromCallable('strlen'); $fn = strlen(...); $fn = Closure::fromCallable([$this, 'method']); $fn = $this->method(...) $fn = Closure::fromCallable([Foo::class, 'method']); $fn = Foo::method(...);
这里,所有的表达式对都是等价的。三点 ( … ) 语法类似于参数解包语法 ( ...$args
)。除了这里,参数尚未填写。
PHP 8.1中的变化
PHP 8.1 还包括对其现有语法和功能的更改。让我们来讨论它们:
- PHP Interactive Shell需要readline扩展
- MySQLi默认错误模式设置为异常
- CSV写入函数的可定制行尾
- 新version_compare运算符限制
- HTML编码和解码函数现在使用ENT_QUOTES | ENT_SUBSTITUTE
- 关于非法紧凑函数调用的警告
- 新的从资源到类对象的迁移
PHP Interactive Shell需要readline扩展
PHP的readline扩展支持交互式shell功能,例如导航、自动完成、编辑等。虽然它与PHP捆绑在一起,但默认情况下并未启用。
您可以使用PHP CLI的-a
命令行选项访问PHP交互式shell:
php -a Interactive shell php > php > echo "Hello"; Hello php > function test() { php { echo "Hello"; php { } php > test(); Hello
在PHP 8.1之前,即使没有启用readline扩展,您也可以使用PHP CLI打开交互式shell 。正如预期的那样,shell的交互功能不起作用,使该-a
选项变得毫无意义。
在PHP 8.1 CLI中,如果您没有启用readline扩展,交互式shell会退出并显示错误消息。
php -a Interactive shell (-a) requires the readline extension.
MySQLi默认错误模式设置为异常
在PHP 8.1之前,MySQLi默认为静默错误。这种行为通常会导致代码不遵循严格的错误/异常处理。开发人员必须实现自己的显式错误处理功能。
PHP 8.1通过将MySQLi的默认错误报告模式设置为抛出异常来更改此行为。
Fatal error: Uncaught mysqli_sql_exception: Connection refused in ...:...
由于这是一个重大更改,对于PHP<8.1版本,您应该mysqli_report
在建立第一个MySQLi连接之前使用该函数显式设置错误处理模式。或者,您可以通过实例化一个mysqli_driver
实例来选择错误报告值来执行相同的操作。
RFC遵循PHP 8.0中引入的类似更改。
CSV写入函数的可定制行尾
在PHP 8.1之前,PHP的内置CSV写入函数fputcsv
和SplFileObject::fputcsv
被硬编码为\n
在每行末尾添加(或换行符)。
PHP8.1为这些函数添加了对名为eol
的新参数的支持。您可以使用它来传递可配置的行尾字符。默认情况下,它仍然使用\n
字符。因此,您可以继续在现有代码中使用它。
标准字符转义规则适用于使用行尾字符。如果您想使用\r
、\n
或\r\n
作为EOL字符,您必须将它们括在双引号中。
这是跟踪此新更改的GitHub页面。
新的 version_compare
运算符限制
PHP的version_compare()
函数比较两个版本号字符串。此函数接受一个可选的第三个参数,operator
用于测试特定关系。
尽管文档中没有明确说明,但在PHP 8.1之前,您可以将此参数设置为部分值(例如g
, l
, n
)而不会出现错误。
PHP 8.1对version_compare()
函数的operator
参数添加了更严格的限制来克服这种情况。您现在可以使用的唯一运算符是:
- ==、=和eq
- !=、<>和ne
- >和gt
- >=和ge
- <和lt
- <=和le
HTML编码和解码函数改用 ENT_QUOTES | ENT_SUBSTITUTE
HTML实体是字符的文本表示,否则会被解释为HTML。想想诸如<
和>
用于定义HTML 标签的字符(例如<a>
、<h3>
、<script>
)。
HTML实体<
是& lt;
(小于符号)和>
是& gt;
(大于符号)。
注意:删除“&”和“amp”之间的空格。
您可以在HTML文档中安全地使用这些HTML实体,而无需触发浏览器的渲染引擎。
例如,& lt;script& gt;
将在浏览器中显示为<script>
,而不是被解释为HTML标记。
之前PHP 8.1,则用htmlspecialchars()和 htmlentities() 函数转换如 “,
<
, >
和&
符号为各自的HTML实体。但默认情况下,他们没有将单引号字符 ('
) 转换为其HTML实体。此外,如果文本中存在格式错误的 UTF-8,它们将返回一个空字符串。
在PHP 8.1中,这些HTML编码和解码函数(及其相关函数)也会默认将单引号字符转换为它们的HTML实体。
如果给定的文本包含无效字符,函数将用Unicode替换字符 (�) 替换它们,而不是返回空字符串。PHP 8.1通过默认将这些函数的签名更改为ENT_QUOTES | ENT_SUBSTITUTE
而不是ENT_COMPAT
来实现这一点。
大多数框架已经ENT_QUOTES
用作默认标志值。因此,由于此更改,您不会看到太大差异。然而,新ENT_SUBSTITUTE
标志并没有被广泛使用。PHP 8.1将导致无效的UTF-8字符被替换为 � 字符而不是返回空字符串。
关于非法紧凑函数调用的警告
PHP 的compact()
函数超级好用。您可以使用它来创建一个数组,其中包含使用名称和值的变量。
例如,考虑以下代码:
$animal = 'Cat'; $sound = 'Meow'; $region = 'Istanbul'; compact('animal', 'sound', 'region'); // ['animal' => "Cat", 'sound' => "Meow", 'region' => "Istanbul"]
紧凑型函数的文档指出,这将只接受字符串参数或字符串值数组值。但是,在PHP 7.3之前,任何未设置的字符串都将被悄悄跳过。
PHP7.3修改了compact()
函数,以便在使用未定义变量时发出通知。PHP 8.1更进一步,并发出警告。
您可以阅读其GitHub 页面以了解此更改是如何发生的。
新的从资源到类对象迁移
PHP的长期目标之一是从资源转向标准类对象。
由于历史原因,资源对象在PHP应用程序中被广泛使用。因此,资源向类对象的迁移需要尽可能减少破坏性。PHP 8.1迁移了五个这样的资源:
将file_info
资源迁移到finfo
对象
PHP的finfo 类为函数提供了一个面向对象的接口fileinfo
。但是,使用finfo
函数返回resource
具有file_info
类型的对象而不是finfo
类本身的实例。
迁移到IMAP\Connection
类对象的IMAP资源
根据资源到对象的迁移目标,当PHP最终修改类的实现细节时,新的IMAP\Connection
类将潜在的破坏性更改降至最低。
这个新类也被声明final
,所以你不被允许extend
。
在其GitHub页面上阅读有关其实现的更多信息。
FTP连接资源现在为FTP\Connection
类对象
在PHP<8.1中,如果您使用或函数ftp_connect()
或者ftp_ssl_connect()
创建FTP连接,您将返回一个ftp类型的资源对象。
PHP 8.1添加了新的FTP\Connection
类来纠正这个问题。和IMAP\Connection
类一样,它也被声明final
为防止它被扩展。
在其GitHub页面上阅读有关其实现的更多信息。
字体标识符迁移到GdFont
类对象
PHP的GD扩展提供了imageloadfont() 函数来加载用户定义的位图并返回其字体标识符资源 ID(一个整数)。
在PHP 8.1中,此函数将改为返回GdFont类实例。此外,为了使迁移轻松自如,以前从imageloadfont()
接受资源ID的所有函数现在都将采用新的GdFont类对象。
在其GitHub页面上阅读有关此迁移的更多信息。
LDAP资源迁移到对象
LDAP或轻量级目录访问协议,用于访问“目录服务器”。就像硬盘目录结构一样,它是一个独特的数据库,以树状结构保存数据。
PHP包含一个LDAP扩展,它接受或返回PHP 8.1之前的资源对象。但是,它们现在都已无缝迁移到新的类实例。已经过渡的资源类型有:
ldap link
资源到\LDAP\Connection
类对象ldap result
资源到\LDAP\Result
类对象ldap result entry
资源到\LDAP\ResultEntry
类对象
浏览其GitHub页面以更好地了解此迁移。
Pspell资源现在为类对象
PHP的Pspell扩展允许您检查拼写和单词建议。
PHP<8.1使用pspell
和pspell config
带有整数标识符的资源对象类型。这两个资源对象现在替换为PSpell\Dictionary
和PSpell\Config
类对象。
与之前的迁移一样,之前接受或返回资源对象标识符的所有Pspell函数都将采用新的类对象实例。
有关更多信息,请参阅其GitHub页面。
PHP 8.1中的弃用
PHP 8.1弃用了许多以前的功能。以下列表简要概述了PHP 8.1弃用的功能:
- 不能将null传递给不可为Null的内部函数参数
- 受限制的$GLOBALS使用
- 内部函数的返回类型声明
- 不推荐使用可序列化接口
- 不兼容的float到int转换已弃用
- mysqli::get_client_info方法和mysqli_get_client_info($param) 已弃用
- 不推荐使用所有mhash*() 函数(散列扩展)
- filter.default和filter.default_options INI设置已弃用
- 在false上弃用autovivification
- 不推荐使用mysqli_driver->driver_version属性
不能将null
传递给不可为Null的内部函数参数
从PHP 8.0开始,它的内部函数null
即使对于不可为null的参数也默默地接受值。这不适用于用户定义的函数——它们只接受null
可为空的参数。
例如,考虑这种用法:
var_dump(str_contains("foobar", null)); // bool(true)
在这里,null
值被静默转换为空字符串。因此,结果返回true
。
该RFC旨在通过在PHP 8.1中抛出弃用警告来同步内部函数的行为。
var_dump(str_contains("foobar", null)); // Deprecated: Passing null to argument of type string is deprecated
在下一个主要的 PHP 版本(即 PHP >=9.0)中,弃用将成为 TypeError,使内部函数的行为与用户定义的函数保持一致。
限制$GLOBALS
使用
PHP的$GLOBALS
变量提供对其内部符号表的直接引用。支持此功能很复杂,并且会影响阵列操作性能。另外,它很少被使用。
根据RFC,$GLOBALS
不再允许间接修改。此更改向后不兼容。
这种变化的影响相对较低:
在前2k个composer包中,我发现了23个使用 $GLOBALS而不直接取消引用它的案例。根据粗略的检查,只有两种情况没有以只读方式使用$GLOBALS。
但是,只读用法$GLOBALS
继续照常工作。不再支持的是$GLOBALS
整体写入。因此,您可能会遇到轻微的性能提升,尤其是在使用普通PHP数组时。
内部函数的返回类型声明
PHP 8.0允许开发人员为大多数内部函数和方法声明参数和返回类型。这要归功于各种RFC,例如内部函数的一致类型错误、联合类型2.0和混合类型v2。
但是,在很多情况下可能会丢失类型信息。其中一些包括具有资源型,out pass-by-ref 参数,非final方法返回类型,函数或不按一般规则解析参数的方法。您可以在其RFC中阅读确切的详细信息。
此RFC仅解决非最终方法的返回类型的问题。然而,PHP团队并没有立即完全淘汰它,而是提供了一个渐进的迁移路径,以使用相关的方法返回类型更新您的代码库。
非最终的内部方法返回类型(如果可能)在PHP 8.1中暂时声明,它们将在PHP 9.0中强制执行。这意味着在PHP 8.x版本中,当内部方法以返回类型不兼容的方式被覆盖时,在继承检查期间会引发“弃用”通知,而PHP 9.0会使这些成为致命错误。
如果您在更新到PHP 8.1后看到此弃用通知,请确保更新您的方法的返回类型。
不推荐使用可序列化接口
PHP 7.4通过两个新的魔术方法引入了自定义对象序列化机制:__serialize()
和__unserialize()
. 这些新方法旨在最终替换损坏的Serializable接口。
该RFC提议通过制定最终删除Serializable的计划来最终确定该决定。
在PHP 8.1中,如果你在没有实现和方法的情况下实现了Serializable接口,PHP将抛出“Deprecated”警告。
在PHP8.1中,如果在没有实现__serialize()
和__unserialize()
方法的情况下实现Serializable接口,PHP将抛出“Deprecated”警告。
Deprecated: The Serializable interface is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in ... on line ...
如果你支持PHP <7.4和PHP >=7.4,你应该实现Serializable接口和新的魔法方法。在PHP >=7.4版本上,魔术方法将优先。
不推荐使用不兼容的float
到int
转换
PHP是一种动态类型语言。因此,在很多情况下自然会发生类型强制。大多数这些强制是无害的,而且超级方便。
但是,当浮点数转换为整数时,通常会涉及数据丢失。例如,当浮点数3.14转换为整数3时,它会丢失其小数值。
当浮点数超出平台整数范围或浮点数字符串转换为整数时,也会发生同样的情况。
PHP 8.1纠正了这种行为,并使其动态类型强制更符合大多数现代编程语言。目标是使这种强制可预测和直观。
在PHP 8.1中,当不兼容的float被隐式强制转换为int时,您将看到弃用通知。但是什么构成了整数兼容的浮点数?RFC对此做出了回答:
如果具有以下特征,则称浮点数与整数兼容:
- 是一个数字(即不是 NaN 或无穷大)
- 在PHP整数范围内(取决于平台)
- 没有小数部分
此弃用通知将在下一个主要PHP版本(即PHP 9.0)中升级为TypeError。
mysqli::get_client_info
和mysqli_get_client_info($param)
方法已过时
MySQL客户端API定义了两个常量:client_info
(字符串)和client_version
(整数)。MySQL Native Driver (MySQLnd) 是官方PHP源代码的一部分,并将这些常量与PHP版本挂钩。在libmysql中,它们代表编译时的客户端库版本。
在PHP8.1之前,mysqli以4种方式公开这些常量:mysqli_driver
属性、mysqli
属性、mysqli_get_client_info()
函数和mysqli::get_client_info
方法。但是,对于client_version
没有方法。
MySQLnd 以两种方式向PHP公开这些常量:常量和函数调用。为了统一使用这两个选项的mysqli访问方法,PHP 8.1弃用了其他两个选项:
get_client_info
mysqli类中的方法。相反,您可以只使用该mysqli_get_client_info()
函数。mysqli_get_client_info()
带参数的函数。不带任何参数调用函数以避免弃用通知。
在其GitHub 页面上阅读有关此弃用的更多信息。
不推荐使用所有mhash*()
函数(散列扩展)
PHP5.3将mhash*()
函数集成到ext/hash中,作为ext/mhash
的兼容层。后来,PHP7.0删除了ext/mhash
。
与hash_*()
函数不同,mhash*()
函数并不总是可用。您必须在配置PHP时单独启用它们。
在PHP 7.4中,散列扩展与PHP捆绑在一起,使其成为PHP的默认扩展。但是,--enable-mhash
出于兼容性原因,它仍然支持启用该选项。
PHP团队决定在PHP 8.1中弃用mhash*()函数,并在PHP 9.0中完全删除它们。不推荐使用的函数是mhash()
,mhash_keygen_s2k()
,mhash_count()
,mhash_get_block_size()
和mhash_get_hash_name()
。您可以使用标准ext/hash
功能代替它们。
filter.default
和filter.default_options
INI设置已过时
PHP的filter.default
INI设置允许您将过滤器适用于所有的PHP超全局-即GPCR的数据($_GET
,$_POST
,$_COOKIE
,$_REQUEST
,和$_SERVER
)。
例如,您可以设置filter.default=magic_quotes
或filter.default=add_slashes
(基于PHP版本)来复活PHP有争议且不安全的魔术引号功能(在PHP 5.4中删除)。
filter.default
的INI设置提供了额外的功能,允许使用更多的过滤器,使情况变得更糟。例如,它的另一个选项-filter.default=special_chars
-只为HTML启用魔法引号。人们对这些设置的了解要少得多。
如果filter.default
设置为除unsafe_raw
(默认值)以外的任何值,PHP8.1将抛出一个弃用警告。您不会看到ilter.default_options
的单独弃用通知,但PHP9.0将删除这两个INI设置。
作为替代方案,您可以开始使用filter_var()函数。它使用指定的过滤器过滤变量。
弃用autovivification
的false
PHP允许自动激活(从假值自动创建数组)。如果变量未定义,此功能非常有用。
尽管如此,当值为false或null时自动创建数组并不理想。
此RFC不允许从错误值自动激活。但是,请注意,仍然允许来自未定义变量和null的自动激活。
在PHP 8.1中,附加到false类型的变量将发出弃用通知:
Deprecated: Automatic conversion of false to array is deprecated in
PHP 9.0同样会抛出致命错误,这与其他标量类型相同。
mysqli_driver->driver_version
属性已弃用
MySQLi 扩展的mysqli_driver->driver_version属性已经13年没有更新了。尽管此后对驱动程序进行了许多更改,但它仍然返回旧的驱动程序版本值,使该属性变得毫无意义。
在PHP 8.1中,mysqli_driver->driver_version属性已弃用。
其他小改动
PHP 8.1中有更多的弃用。将它们全部列出在这里将是一项令人筋疲力尽的工作。我们建议您直接查看RFC以了解这些较小的弃用情况。
PHP的GitHub页面还包括一个PHP 8.1升级说明指南。它列出了在升级到PHP 8.1之前您应该考虑的所有重大更改。
小结
PHP 8.1离我们不远了。并且它已经承诺将其前身提升一倍,这是不小的壮举。
我们认为最令人兴奋的PHP 8.1特性是枚举、纤维、纯交集类型及其许多性能改进。此外,我们迫不及待地要让PHP 8.1步入正轨,并对各种PHP框架和CMS进行基准测试。
评论留言