PHP 8.x 深入探讨PHP性能改进特性

作为一个充满激情的开发人员,我很高兴地见证了PHP这个广受赞誉的服务器端脚本语言在过去二十年中的发展。

现在,随着PHP 8.x的发布,PHP作为世界各地开发人员强大而可靠的选择的主导地位比以往任何时候都要强大。PHP 8.x系列,包括PHP 8.0,PHP 8.1,以及截至2022年11月的PHP 8.2,推出了大量的增强功能和新功能,但最值得注意的是,与其前辈相比,它提供了显着的性能提升。

在本文中,我很高兴能够深入探讨PHP 8.x带来的性能改进和高效处理。通过彻底检查和分析这些前沿的增强功能,我的目标是为开发人员和技术爱好者提供宝贵的见解,以了解PHP 8.x中的关键优化。

这一全面的探索将使您能够充分掌握拥抱PHP 8.x的好处,并了解其对您的Web应用程序和整个PHP生态系统的潜在影响。所以,让我们深入了解PHP 8.x为我们准备的显著进步。

PHP 8.x系列始于2020年11月26日发布的PHP 8.0,随后是2021年11月25日发布的PHP 8.1。这些版本引入了几个突破性的功能,以及实质性的性能改进,对Web开发环境产生了积极的影响。

PHP 8.0中值得注意的特性包括即时(JIT)查询器、属性(也称为注释)、命名参数、nullsafe运算符和匹配表达式。PHP 8.1继续构建在PHP 8.0奠定的坚实基础上,引入了新的特性,如枚举,只读属性,纤程(为了更好的并发支持),以及新的语法改进,如使用字符串键和第一类可调用的数组解包。

PHP 8.2引入了许多新特性,包括:

  • Co-routines:Co-routines是一个新特性,它允许PHP开发人员编写并发代码,而不需要线程。
  • 联合类型:联合类型是一个新特性,允许PHP开发人员定义一个变量来保存一组类型中的任何一个。
  • 改进的错误处理:PHP 8.2对错误处理进行了许多改进,包括更好的错误消息和更优雅地处理错误的能力。

在PHP 8.x中,属性已经取代了用于添加元数据的效率较低且容易出错的docblock注释,这显著影响了PHP性能。通过提供对属性的原生支持,PHP 8.x确保了在编译时对其进行验证,消除了可能由不正确的元数据引起的运行时问题。

这种标准化的方法鼓励PHP社区采用元数据和流程管理的最佳实践,最终提高PHP的整体性能和Web服务器应用程序的效率。

此外,属性由于其元数据提取的标准化方法,促进了与各种开发工具、监控工具和框架的更好集成。这就增强了IDE、静态分析器和其他开发工具的支持,优化了PHP性能,丰富了开发体验。

因此,属性不仅提高了PHP代码的质量,而且还促进了更高效和更精简的开发过程,从而在Web服务器上实现了性能更好的PHP应用程序。

考虑一个在Web应用程序中验证用户输入的简单用例。以前,开发人员会使用docblock注释来提供关于验证规则的元数据,然后在运行时解析这些元数据。

这可能会导致性能开销和潜在的运行时错误。使用PHP 8.x属性,开发人员现在可以直接向代码添加元数据,并在编译时进行验证。

下面是一个比较旧的docblock annotation方法和新的attributes方法的例子:

PHP 7.x及更早版本

PHP 8.x

在上面的示例中,PHP 8.x基于属性的方法提供了一种更干净、更有效的方法来向EmailValidator类添加元数据。通过利用属性,开发人员可以消除与解析docblock注释相关的运行时开销,从而提高PHP性能和更高效的Web服务器应用程序。

PHP 8.x中的命名参数提供了一定程度的灵活性,在修改或扩展函数时会显著影响PHP的性能。通过显式命名参数,开发人员可以轻松地添加、删除或更改参数的顺序,而不会破坏现有的PHP代码。

这鼓励代码的可重用性和模块化设计,以及简化对每个参数的目的的理解。最终,命名参数有助于更好地维护和高效的PHP代码库,提高PHP在Web服务器上的性能。

通过使用命名参数,开发人员可以创建更具表现力和自文档化的API,这对于PHP性能调优至关重要。此功能有助于理解复杂函数,特别是在处理大量参数、默认值或可选参数时。

因此,命名参数有助于减少开发人员的认知负荷,使编写、审查和调试PHP代码变得更加容易。这导致了更健壮和高效的PHP应用程序的开发,进一步增强了PHP在Web服务器上的性能。

无命名参数(PHP 7.x及更早版本)

使用命名参数(PHP 8.x):

在此示例中,由于未显式指定fromisHtml参数,因此将跳过它们。将使用这些参数的默认值。

这是提高代码可读性和可维护性的好方法。通过使用命名参数,可以明确哪些参数是必需的,哪些参数是可选的。这可以帮助减少错误并使代码更可重用。

以下是使用命名参数时需要记住的其他一些事情:

  • 必须在任何位置参数之后使用命名参数。
  • 不能两次使用同一个参数名。
  • 如果没有为可选参数指定值,则将使用默认值。
  • 如果为可选参数指定值,则默认值将被忽略。

通过简化可空对象的处理,nullsafe操作符允许开发人员编写更优雅、更少出错的PHP代码,从而增强PHP在Web服务器上的性能。

这可以带来显著的改进,因为它减少了处理空值所需的条件逻辑的数量。在最新的PHP版本上运行的PHP应用程序可以减少共享内存占用量并缩短执行时间,进一步增强PHP 8.x中此功能的优势。

nullsafe操作符还鼓励在PHP应用程序中采用更一致的错误处理方法,特别是在处理数据库查询和SQL查询时。通过推广可空类型的使用并减少处理空值所需的样板代码,开发人员可以创建更干净、更可维护的代码库。

这种处理可空对象和空相关错误的一致方法可以产生更稳定和更高性能的应用程序,并在PHP社区中培养更统一的编码风格。

不使用Nullsafe运算符(PHP 7.x及更早版本):

使用Nullsafe Operator(PHP 8.x):

在上面的示例中,PHP 8.x nullsafe运算符(?->)简化了可空对象的处理,并减少了所需的条件逻辑的数量。这会产生更优雅、可读性更强、更易于维护的代码,有助于在Web服务器上提高PHP性能。

PHP 8.x中匹配表达式的引入使开发人员能够以更有效和可读的方式处理复杂的条件逻辑,这可以显着提高PHP在Web服务器上的性能。通过消除对break语句的需求并提供详尽的匹配,减少了人为错误的可能性。

匹配表达式不仅提高了代码的可读性,而且还支持更高级的模式匹配功能。通过添加匹配表达式,开发人员现在可以以更简洁和优雅的方式表达复杂的条件和模式匹配。

这可能会导致更多的维护代码,因为开发人员可以轻松地理解和修改复杂的逻辑,而不会迷失在if-else语句或switch案例的海洋中。因此,匹配表达式有助于PHP Web应用程序的整体效率和性能。

为了提高PHP性能,PHP 8.1中引入的枚举在代码可读性、可维护性和运行在Web服务器上的Web应用程序的效率方面提供了显著的优势。

通过将字符串或常量值替换为枚举,开发人员可以减少共享内存的使用,提高值比较的速度,从而提高代码执行效率,并最终提高Web应用程序的PHP性能。

在最新的PHP版本中引入枚举还使开发人员能够创建更有意义和更具表现力的API,这可以改善开发团队之间的沟通和协作。

枚举作为一种文档形式,使开发人员更容易理解预期值及其用途。通过将枚举合并到代码库中,开发人员可以减少误解和错误的可能性,进一步提高应用程序性能和PHP Web应用程序的整体可靠性。

专注于PHP性能,在Web服务器上运行的PHP应用程序中实现只读属性允许开发人员避免与可变状态管理相关的性能开销。

通过强制不变性,对代码库中对象行为的推理变得更简单,有助于最小化错误并提高代码的可维护性。因此,只读属性有助于更稳定和更高性能的Web应用程序。

在PHP应用程序中添加只读属性还可以增强原生PHP函数的安全性以及数据库查询的处理和处理时间。通过强制执行不变性,开发人员可以降低对类属性进行未经授权修改的风险,这可能有助于防止某些类型的漏洞。

此外,只读属性有助于在Web应用程序的不同组件之间建立清晰的边界和契约,从而促进更模块化和更安全的代码库。这反过来又为PHP Web应用程序带来了更好的性能和稳定性。

为了强调PHP的性能,PHP 8.x中引入的Fiber标志着在Web服务器上运行的PHP应用程序的并发管理方面的重大变化。与传统线程相比,纤程的轻量级特性最小化了与上下文切换相关的开销。

这会带来更好的资源利用率和更高的吞吐量,特别是在具有高并发需求的Web应用程序中。通过实现纤程,开发人员可以创建更高性能和可伸缩的PHP应用程序,突出了PHP 8.x的进步。

在PHP 8.x中采用纤程有可能改变PHP开发人员在Web应用程序中管理并发和并行的方式,包括那些涉及数据库查询和处理时间的应用程序。

使用Fibers,开发人员可以构建响应更快、更高效的PHP应用程序,从而更好地利用现代硬件资源,如多核处理器。此外,纤程支持更容易访问的异步编程方法,吸引了更广泛的开发人员。

因此,纤程在开发更高性能、可伸缩性和可维护性的PHP应用程序方面发挥着至关重要的作用,展示了PHP生态系统的持续发展。

这些语法改进通过允许开发人员更有效地执行数组操作和操作可调用对象来简化和精简应用程序代码。这些特性有助于提高代码的可读性和可维护性。

这些改进不仅通过更快的加载时间和更短的延迟使最终用户受益,而且还减少了服务器资源消耗,为企业和组织节省了成本。总的来说,这些功能有助于使PHP 8.x成为Web开发人员更强大,更有表现力和更高效的选择。

PHP 8.x对性能改进的关注可以追溯到对更快、更高效的Web应用程序的需求。随着互联网的快速发展和用户期望的不断提高,开发人员一直在寻求优化应用程序性能的方法。

PHP 8.x通过在语言级别提供大量优化来满足这一需求,允许开发人员提供更快,响应更快的Web应用程序,而无需大量的应用程序代码重构。这些改进不仅使最终用户受益,还减少了服务器资源消耗,最终为企业和组织节省了成本。

Just-In-Time(JIT)编译器是PHP 8.x中引入的最重要的性能改进特性之一。作为PHP 8.0版本的关键组件,JIT编译器旨在提高PHP脚本的执行速度,使其成为该语言性能的游戏规则改变者。

JIT编译器的工作原理是在运行时将PHP代码中频繁执行的部分转换为机器代码,而不是传统的动态解释PHP代码的方法。编译后的机器代码的执行速度比解释后的代码快得多,从而大大提高了性能。

JIT编译器以两种模式运行:函数和跟踪JIT。函数JIT编译整个函数,而跟踪JIT编译频繁执行的较小代码路径。

要了解JIT编译器对实际应用程序的影响,必须认识到并非所有PHP应用程序都能看到相同程度的性能改进。JIT编译器的优势在CPU密集型任务中变得更加明显,例如数学计算、图像处理和复杂的数据操作。

对于许多典型的Web应用程序,性能改进可能不太明显,因为这些应用程序通常是I/O绑定的,这意味着瓶颈与数据库查询和文件系统访问等输入/输出操作有关,而不是CPU绑定的任务。

然而,JIT编译器的引入仍然有潜力为PHP在其他领域中打开新的可能性并提高性能。例如,PHP可能成为高性能计算、机器学习和实时应用程序的一个更可行的选择,在这些应用程序中,提高的执行速度可能会产生重大影响。

在PHP 8.x中,对函数调用和类型声明进行了一些改进,有助于在为Web服务器和Web服务开发PHP应用程序时增强性能和更有效的代码执行。这些优化包括以下内容

PHP 8.x为内部函数和用户定义函数引入了更有效的类型检查。这种改进是通过在编译阶段更好地处理类型检查来实现的,这减少了与运行时类型检查相关的开销。此外,PHP 8.x还优化了联合类型的检查方式,从而进一步提高了性能。

类型检查的这些增强有助于提高PHP应用程序的整体性能,帮助消除可能由低效类型处理引起的潜在瓶颈和性能问题。

此外,改进的类型检查可以减少数据库负载,降低查询速度和改善业务逻辑,因为它确保只处理和存储有效数据,从而提高数据库操作和SQL查询的效率。

通过专注于类型检查优化,PHP 8.x使开发人员能够微调PHP性能,并创建更健壮和更高效的应用程序,可以更好地处理现代Web服务器和动态Web页面的需求。

在PHP 8.x版中,引入了对内部函数和用户定义函数进行更有效的类型检查,从而显著提高了性能。通过在编译阶段优化类型检查,PHP 8.x还减少了与运行时类型检查相关的开销,同时还增强了对联合类型的处理。这些增强功能有助于提高应用程序的整体性能,这对于需要快速加载时间和响应能力的Web应用程序至关重要。

改进的类型检查有助于消除可能由低效类型处理引起的潜在瓶颈和性能问题,从而使Web应用程序更加稳定和高效。此外,这种优化确保只处理和存储有效数据,从而提高数据库操作的效率并减少页面加载时间。

通过对类型检查优化的关注,PHP 8.x为开发人员提供了工具来微调应用程序的性能,并创建更健壮、更高效的Web应用程序。这在性能基准测试的上下文中特别有益,因为它允许开发人员准确地测量和比较其应用程序的性能与行业标准或竞争对手。

通过利用这些性能改进,开发人员可以更快地编写代码,构建更好地满足现代Web服务器需求的Web应用程序,并提供更好的用户体验。

PHP 8.x及以后版本中引入的命名参数提供了改进代码优化和可读性之外的好处。它们还可以在某些情况下有助于性能优化。通过使用命名参数,开发人员可以在调用函数时跳过不必要的参数,从而减少需要处理的数据量。

这些优化通过最小化与函数调用和类型声明相关的开销来增强性能。通过优化语言的这些基本方面,PHP 8.x可以更有效地执行代码,从而缩短加载时间并提高Web应用程序的整体性能。

这些改进对实际应用程序的影响取决于特定的用例和编码实践。具有大量函数调用和广泛使用类型声明的应用程序可能会获得更显著的性能提升。

总的来说,这些优化有助于提高响应速度和效率的用户体验,并减少服务器资源消耗,通过解决性能瓶颈和增强Web服务器上的应用程序性能,使最终用户和企业受益。

从旧版本迁移到PHP 8.x及更高版本可以提供显著的性能改进,但开发人员也可能会遇到兼容性方面的挑战,这是由于不推荐使用的功能、函数行为的更改或新语法和功能的引入。

为了确保顺利的迁移过程,开发人员应该意识到这些潜在的问题,并采用最佳实践来最大限度地减少中断。以下是迁移到PHP 8.x时需要注意的事项的综合列表。

PHP 8.x及以后的版本已经删除了几个扩展和函数,这些扩展和函数要么被弃用,要么有更好的替代品。如果您的代码依赖于这些删除,则这些删除可能会导致兼容性问题。例如 ext/ereg扩展已经被删除,以支持更高效的ext/pcre扩展。

准备好重构代码以进行代码优化,从而使用更新的函数或替代方案。

删除的扩展和函数示例

在PHP 8.x中删除了functionfunction()函数。如果你的代码使用了匿名函数(),你需要重构它来使用匿名函数(闭包)。

PHP 8.x引入了错误报告和处理的变化,将许多运行时通知,警告和错误转换为更严重的异常。这一更改意味着以前在代码中隐藏的问题现在可能会导致未处理的异常,导致PHP应用程序出现意外行为或过早终止。检查错误处理和日志记录策略,确保它们与PHP 8.x兼容。

错误报告和处理示例中的更改:

在PHP 8.x中,传递给函数的参数太少将引发TypeError异常而不是警告。要处理此更改,您应该更新错误处理代码以捕获TypeError异常。

PHP 8.x对几个核心类进行了更改,这可能会导致兼容性问题。例如,Serializable接口已被弃用,并被serialize()unserialize()魔术方法所取代。如果您的代码依赖于实现Serializable接口的类,则需要相应地更新它们。

核心类中的不兼容更改示例:

如前所述,Serializable接口已被弃用。如果你有一个实现这个接口的类,你应该更新它以使用serialize()unserialize()魔术方法。

PHP 8.x中的某些函数和方法更新了签名,如果您的代码依赖于旧的签名,这可能会导致兼容性问题。

例如,PHP 8.0引入了对反射API中各种方法的签名的更改,例如:ReflectionMethod::invoke()方法。准备好更新代码以符合新的函数和方法签名。

函数和方法签名的更改示例:

ReflectionParameter::getClass()方法在PHP 8.x中已被弃用,您应该改用ReflectionParameter::getType()方法。

PHP 8.x更改了一些函数和特性的默认行为,这可能会导致兼容性问题。

例如,默认错误报告级别已更改为包括所有错误、通知和警告。如果依赖以前的默认错误报告级别,此更改可能会导致代码中出现意外行为。

默认行为更改示例:

在PHP 8.x中,strip_tags()函数现在默认从输入字符串中删除前导和尾随空格。如果您的代码依赖于前面的行为,则可能需要对其进行相应的调整。

PHP 8.x对某些语言结构进行了更改,这可能会导致兼容性问题。

例如,switch构造现在支持严格的类型检查,如果您的代码不符合新的要求,这可能会导致类型错误。准备好更新代码以符合语言构造中的更改。

语言结构的变化示例:

在PHP 8.x中,switch结构使用严格的类型检查,这意味着不相同的类型将不再匹配。更新代码以使用严格类型,或将值显式强制转换为所需类型。

PHP 7和PHP 8的性能比较

PHP 8.x引入了许多性能改进,使其成为Web开发的强大工具。您几乎可以将本文的内容用作php性能调优提示指南。

即时(JIT)编译器、优化的函数调用和类型声明有助于更快、更高效的Web应用程序,而命名参数、属性和匹配表达式等新语言功能则提供了更高的性能和可读性。

迁移到PHP 8.x还需要解决潜在的兼容性问题,例如删除的扩展和函数,错误报告和处理的更改,核心类的更新,以及函数和方法签名,默认行为和语言结构的更改。仔细的规划和遵循最佳实践可以确保平稳的过渡,并帮助开发人员充分利用PHP 8.x的性能增强。

如果您正在考虑迁移到PHP 8.x或希望优化现有的PHP应用程序,那么Jupiter可以提供帮助。我们的专家团队专注于调整Web性能关键指标、扩展Web应用程序、降低开发成本、缩短上市时间、改善用户体验、保护Web应用程序、确保质量和可靠性以及维护Web产品。

PHP 8.3 新特性解读

本文是 PHP 8.x 系列文章的一部分。你可以通过订阅 RSS 来接收有关本系列文章的更新通知。PHP 仍然是互联网上使用最广泛的脚本语言之一,w3tech 的数据显示,有 77.3% 使用服务器端编程语言的网站都在使用它。PHP 8 带来了许多新特性和改进,我们将在本系列文章中进行探讨。

PHP 8.3 是 PHP 8.x 系列最新的主要更新版本。

除了性能改进之外,它还带来了许多新特性,包括修正了在 PHP 8.1 中引入的 readonly 特性;显式类型化的类常量;一个新的用于标记覆盖超类方法的 #[\\Override] 属性,等等。

环境设置

下载并安装 PHP 8.3 二进制文件。在本系列前几篇文章中,我们使用了 Windows 操作系统。为了与此保持一致,请下载并安装 PHP 8.3 Windows 二进制文件。按照 PHP 7——入门及面向对象编程改进中的说明来设置环境。最后,在命令行运行 php –version 验证 PHP 版本是否为 8.3。

新的 increment 和 decrement 运算符

PHP 8.3 引入了新的增减函数 str_increment(string $string) 和 str_decrement(string $string),它们通过加减 1 来实现对参数的增减操作。换句话说,$v++ 等同于 $v += 1,$v– 等同于 $v -= 1。

对于以下任一情况,函数将抛出 ValueError:

  • $string 为空字符串;
  • $string 不是由字母和数字 ASCII 字符组成。

另外,如果字符串无法执行减操作,str_decrement 函数会抛出 ValueError。例如,“A”或“0”无法再减。对非字母数字字符串的增减操作已被弃用。可以被表示为科学记数法的数字字符串不执行类型转换。

在下面的示例脚本中,str_increment(string $string) 函数调用将对一个字母数字字符串的值进行增操作。str_decrement(string $string) 函数将对字母数字字符串的值进行减操作。脚本还演示了函数的参数必须是字母数字字符串,否则将抛出 ValueError:

运行脚本,得到以下输出:

对bool类型的增减操作不会有任何效果,但会生成警告。同样,对空字符串的增减操作已被弃用。此外需要注意的是,增减非数字字符串都没有效果,并已被弃用。作为演示,请运行以下脚本:

输出包含了弃用消息:

不过,字母数字字符串可以被增减,尽管输出可能并不总是可预测的。运行下面的脚本:

输出如下:

字符串参数必须在范围内,以免出现溢出。作为演示,运行下面的脚本:

输出了ValueError:

新的#[\\Override] 属性

PHP 8.3 引入#[\\Override] 属性,用于显式声明覆盖方法。新引入的#[\\Override] 属性用于消除在方法覆盖方面存在的歧义。方法覆盖声明可能会有什么歧义?PHP 会验证覆盖方法的签名与父类中被覆盖的方法是否兼容以及从接口继承的实现方法与给定接口是否兼容。PHP 不会验证一个方法是否打算重写父类已有的方法。

PHP 不验证一个方法是否打算实现接口中的方法。如果使用新的#[\\Override] 属性声明了意图,那么对于因方法签名相似性、拼写错误导致被误认为是重写方法而实际上并非如此的代码,都更容易进行调试。显式标记覆盖方法(无论是来自超类还是接口)可用于许多目的,包括:

  • 使调试更容易。
  • 重构和清理已有代码。
  • 检测由开发库提供的超类中可能产生的破坏性变更。

PHP 引擎是如何解释新的#[\\Override] 属性的?如果该属性被添加到方法中,引擎在编译时会验证父类或实现的接口中是否存在同名方法。如果没有这样的方法,就会生成编译时错误。#[\\Override] 属性不会改变覆盖方法的规则和语法,它只是向编译器提供了一个提示。#[\\Override] 属性将在以下几种情况下发生作用:

  • 超类或接口公共和受保护的方法,包括抽象方法和静态方法。
  • 使用的 trait 的抽象方法(使用的 trait是指在类中通过use关键字来使用的 trait),如随后的示例所示。

在下面的示例脚本中,类 B 扩展了类 A 并覆盖了三个方法fn1、fn2和fn3。

运行脚本后,前两个方法满足#[\\Override] 属性的条件,但fn3不满足,因为fn3是一个私有方法。

在下面的示例中,一个类扩展了另一个类并实现了一个接口,覆盖了其唯一的方法。#[\\Override] 属性放在覆盖方法上。

超类中必须存在匹配的方法。作为演示,请运行下面的脚本,其中#[\\Override] 属性放置在没有与超类匹配的方法上。

这将生成错误消息:

#[\\Override] 属性无法被应用于__construct()方法,如下面的脚本所示:

这将生成错误消息:

如果 trait 没有被用在类中,那么 trait 方法上的#[\\Override] 属性将被忽略。可以在 trait 的方法上声明#[\\Override] 属性,如下所示:

但是,如果 trait 在类中被使用,则不能在 trait 的方法上声明#[\\Override] 属性,除非该方法也存在于超类中。例如:

这将生成错误消息:

当然,并不是所有来自父类、接口或被使用的 trait 的方法都必须被覆盖。如果没有提供实现,从父类、接口或 trait 继承了抽象方法的类可以被声明为抽象的。但是,当一个类确实覆盖了来自被使用的 trait、接口或超类的方法时,最好(尽管不是必须的)用#[\\Override] 属性标记覆盖的方法。

在类中重写了来自使用的 trait 的抽象方法满足#[\\Override] 属性。这意味着从类中使用的 trait 继承的抽象方法可以在类中使用#[\\Override] 属性进行标记,表明这是一个覆盖的方法。在下面的示例脚本中,方法hello上的#[\\Override] 属性表明了打算覆盖 trait 的hello()抽象方法的意图。

不过,从使用的 trait 继承的普通方法不能使用#[\\Override] 属性标记,因为它实际上并不会覆盖任何方法,只是“遮蔽”来自 trait 的同名方法。作为演示,请看下面的脚本:

#[\\Override] 属性表明了有意覆盖某些方法,但该类只是在“遮蔽”一个与 trait 中同名的方法。下面的脚本将生成错误消息:

#[\\Override] 属性可以与枚举一起使用。例如,声明一个接口,并在枚举中实现该接口,然后在枚举中覆盖接口的方法。

#[\\Override] 属性可以与匿名类一起使用。例如:

脚本的输出如下:

任意静态变量初始化器

PHP 8.3 增加了对静态变量初始化器中非常量表达式的支持。在下面的示例中,fn2()中的静态变量初始化器是一个函数调用,而不是一个常量。

当调用该函数时,脚本返回值为 5。

在 PHP 8.3 之前支持的重新声明静态变量在 PHP 8.3 中不再受支持。下面的脚本重新声明了一个静态变量初始化器。

当运行脚本时,将生成错误消息:

支持非常量表达式的一个副作用是,ReflectionFunction::getStaticVariables()方法可能无法确定静态变量的值,因为静态变量初始化器使用的表达式的值仅在调用函数后才知道。如果在编译时无法确定静态变量的值,则返回NULL值,如下面的示例所示:

接下来,修改脚本,让它调用 getInitValue 函数,该函数会初始化静态变量:

这次,同样的ReflectionFunction调用返回int(1)的初始化值。

但是,一旦值被添加到静态变量表中,它就不能用另一个函数调用来重新初始化,例如:

静态变量的值仍然是int(1),如下面脚本的输出所示:int(1) int(1)。

允许在静态变量初始化器中使用非常量表达式的另一个副作用是,如果在初始化过程中抛出异常,则静态变量不会被显式初始化,且初始值为NULL,但后续的调用可能会初始化静态变量。

另一个副作用是,依赖于另一个静态变量的静态变量的初始值在编译时是未知的。在下面的脚本中,静态变量$b的值仅在调用setInitValue()之后才知道。

输出是:

动态类常量查找

PHP 8.3 引入了新的查找类常量的语法。在 PHP 8.3 之前,必须使用constant()函数来查找类常量,如下所示:

输出是:

在 PHP 8.3 中,查找类常量的语法简化如下:

输出是:

新的只读特性

正如我们在本系列之前的文章中所描述的,readonly属性是在 PHP 8.1 中引入的,而readonly类是在 PHP 8.2 中添加的。PHP 8.3 通过添加两个新特性进一步扩展了readonly的功能:

  • 在克隆期间,可以重新初始化只读属性。
  • 非只读类可以扩展只读类。

可以在克隆过程中重新初始化只读属性

对于readonly属性的深度克隆,可以在克隆过程中重新初始化readonly属性。我们先从一个深度克隆示例开始,该示例在使用 PHP 8.2 运行时会失败。下面脚本中的readonly属性是RO::$c。

当运行脚本时,将生成错误信息:

下面的脚本演示了在 PHP 8.3 中修改readonly属性。

输出如下:

重新初始化只在调用__clone()方法期间执行。被克隆的原始对象不会被修改,只有新实例可以被修改。因此,从技术上讲,对象仍然是不变的。重新初始化只能执行一次。取消readonly属性的赋值也被视为重新初始化。

在下面的示例中,类A声明了两个readonly属性$a和$b,它们由__construct()函数初始化。__clone()方法重新初始化了readonly属性$a,而readonly属性$b通过调用cloneB()函数取消赋值。

克隆对象并修改其readonly属性不会更改原始对象的readonly属性值。

readonly属性保持相同的值,分别为 1 和 2。

重新初始化相同的readonly属性两次会报错。例如,如果将下面的代码行添加到__clone()方法中:

这将生成以下错误消息:

非只读类可以扩展只读类

在 PHP 8.3 中,非readonly类可以扩展readonly类。例如,下面的脚本声明了一个readonly类A,其中包含了三个隐式readonly的属性。readonly属性在类构造函数中初始化。

然后,非readonly的类B扩展了类A。

在 PHP 8.2 中,这个类将会报错:

但是,在扩展类中不能重新定义readonly类A中的属性,因为这些属性隐式为readonly。

一个readonly类仍然无法扩展一个非readonly类。

非readonly类扩展readonly类不会使扩展类隐式成为readonly的。

虽然readonly类不能声明无类型属性或静态属性,但非readonly类扩展readonly类可以声明无类型属性或静态属性,如下面的脚本所示:

类型化类常量

PHP 8.3 增加了对类型化类常量的支持。类型化类常量可以添加到类、接口、枚举和 trait 中。类型化类常量意味着类常量可以与显式类型关联。

在 PHP 8.3 之前,类常量没有显式类型,因此子类可以分配与定义类中使用的类型不同的类型。在 PHP 8.3 中,常量可以被类型化,例如使用string类型。即使在派生类中,string类型的常量只能被赋string值,而不能被赋其他类型的值。

在下面的示例中,将int类型的常量赋为string值。

这将生成错误消息:

mixed类型可以像下面这样赋值给常量:

除了void、never和callable之外,任何 PHP 类型都可以赋值给类常量。

Randomizer 类的新增内容

PHP 8.3 向\\Random\\Randomizer类添加了三个新方法。这些方法提供了常见的功能。其中一个函数从给定的字符串生成随机选择的字节,另外两个函数生成随机浮点数。

新的 Randomizer::getBytesFromString() 方法

这个方法返回一个指定长度、由给定字符串中随机选择字节组成的字符串。

方法定义如下:

下面是调用这个方法的示例脚本:

输出为:

新的 Randomizer::getFloat() 和 Randomizer::nextFloat() 方法

getFloat() 方法返回一个介于指定的最小值和最大值之间的随机浮点数。

$boundary 参数值决定了 $min 和 $max 值是否包含在内。换句话说,$boundary 参数决定了返回的值是否可以是 $min 或 $max。

枚举值决定了间隔,如下表所示:

例如,getFloat() 返回介于 1 和 2 之间的随机浮点数:

输出如下:

$max 参数必须大于 $min 参数,但必须是有限的。

下面的示例演示了如何指定边界。

多次调用脚本生成的输出为:

nextFloat() 方法返回一个介于 [0, 1) 的随机浮点数。这个方法等同于getFloat(0, 1, \\Random\\IntervalBoundary::ClosedOpen)。

unserialize() 针对尾部数据

生成警告消息

unserialize() 函数之前只考虑了主要数据,忽略了序列化值尾部分隔符后的数据,即用于标量的‘;‘和用于数组和对象的‘}’。在 PHP 8.3 中,尾部的字节不再被忽略,它会输出一条警告消息,例如:

这将生成警告消息:

新的 json_validate() 函数

PHP 8.3 添加了一个非常有用的新函数,用于验证字符串参数是否为有效的 JSON。字符串参数必须是 UTF-8 编码的字符串。该函数返回一个布尔值(true 或 false),表示字符串是否为有效的 JSON。在 PHP 8.3 之前,需要创建一个自定义函数来验证 JSON,如下所示:

但 json_validate() 不是内置函数。下面这个不包含自定义函数定义的脚本在 php 8.3 之前版本运行时会报错:

错误消息为:

有了 PHP 8.3 中新的 json_validate() 函数的支持,下面的脚本可以正常运行:

输出为:

被弃用的小功能

PHP 8.3 弃用了一些未被使用的小功能。

将负的 $widths 传给 mb_strimwidth() 已被弃用。要使用这个函数,必须在 php.ini 配置文件中启用多字节扩展:

下面的脚本向函数传入 -2。

在运行脚本时,将输出弃用消息:

其次,NumberFormatter::TYPE_CURRENCY 常量已被弃用。用使用这个常量,需要启用国际化扩展。

运行下面的脚本:

将输出弃用消息:

MT_RAND_PHP 常量是为特殊情况实现而引入的,没有任何重要的用途,因此已被弃用。运行下面使用该常量的脚本。

将输出弃用消息:

ldap_connect 函数,用于检查给定的连接参数是否可以连接到 LDAP 服务器,已弃用单独指定主机和端口的函数签名:

要使用这个函数,需要在 php.ini 中启用 ldap 扩展。

运行下面的脚本:

将输出弃用消息:

总 结

回顾一下,本文讨论了 PHP 8.3 中的一些重要新特性,包括对之前 8.x 版本中引入的只读特性的修正、用于显式表达覆盖方法意图的#[\\Override] 属性、显式类型化的类常量,以及新的用于验证 JSON 字符串的json_validate()函数。

本文是 PHP 8.x 系列文章的一部分。你可以通过订阅 RSS 来接收有关本系列文章的更新通知。

PHP 仍然是互联网上使用最广泛的脚本语言之一,w3tech 的数据显示,有 77.3% 使用服务器端编程语言的网站都在使用它。PHP 8 带来了许多新特性和改进,我们将在本系列文章中进行探讨。

查看英文原文:

https://www.infoq.com/articles/whats-new-php-8-3/

PHP8知识详解:PHP8的新特性

PHP 8是PHP编程语言的一个主要版本,在2020年11月26日发布。它引入了许多新特性和改进,包括以下一些主要特性:

1. JIT 编译器:PHP 8引入了名为Tracing JIT的即时(Just-In-Time)编译器。JIT可以将PHP脚本中频繁执行的部分编译成原生机器码,提高执行速度。

2. 类型系统改进:PHP 8推出了更强大的类型系统,支持函数参数与返回值定义严格的静态类型,并且支持联合类型、类属性类型声明和对任意数据类型使用点运算符。

3. 匿名类特性增强:匿名类现在可以通过关键字`new`实例化,并且支持从其他类继承。

4. 其他语言和表达式改进:PHP 8带来了许多语法和表达式的改进,如正则表达式新增的match()函数、nullsafe操作符(?->)等。

5. 错误处理改进:PHP 8引入了新的Throwable接口作为异常基类,使得所有错误和异常都成为可捕获的。

6. 函数签名特性:通过Function Union、Callable Variance、Mixed Type Hinting等功能,PHP 8允许以更灵活和精确的方式定义函数的参数和返回类型。

7. 弱类型模式的改变:在PHP 8中,弱类型模式发生了一些改变。例如,在数值比较和字符串到数字的转换方面,现在更加严格。

这只是PHP 8提供的一些主要新特性。其他还有很多小的改进和增强,如性能优化、标准库更新等。您可以查阅官方文档来详细了解PHP 8的所有新功能。

PHP服务网提醒你:升级到PHP 8可能需要对现有代码进行适应和调整,以适应新的语法和特性。因此,在升级之前,请确保你的代码与PHP 8兼容,并进行充分的测试。

想学习更多的PHP知识,敬请关注PHP服务网将持续更新的《PHP8知识详解》系列教程,本站将详细讲述使用PHP8+ThinkPHP8+MySQL8打造一个属于自己著作权的内容管理系统(CMS)。

相关阅读:认识PHP8

PHP是什么

PHP语言优势

PHP8的新特性

PHP8的应用领域

PHP的历史版本

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。