PHP 7 简介:新功能和新功能

已发表: 2022-03-11

2015 年 PHP 世界中最激动人心的事件之一是 PHP 7 的发布,距离上一个主要版本 PHP 5 的发布已经过去了 10 年。随着向前迈出一大步,PHP 7 引入了大量新功能和性能升级.

但是,它也删除了旧的、已弃用的功能,这引入了一些兼容性中断,使旧应用程序更难迁移到新版本。 如果您计划在 PHP 7 之上移动现有应用程序或构建新应用程序,本指南应该作为一个快速浏览。

但是等等,PHP 6 去哪儿了?

如果您最近没有使用 PHP,您可能想知道 PHP 6 发生了什么,为什么从 PHP 5 跳到 PHP 7? 好吧,长话短说,PHP 6 失败了。 版本 6 的主要特点是对 Unicode 字符的原生支持,因为 PHP 主要用于 Web 开发并且 Web 需要 Unicode,因此将 Unicode 引入 PHP 的举动是有意义的。

这个想法是为核心本身带来对 Unicode 的完全支持。 它将为该语言带来扩展的功能,从使用愚蠢的表情符号作为变量和函数名称的能力,到强大的国际字符串功能。 例如,当另一种语言使用与英文不同的大小写字母时,或者当需要将汉字名称转换为英文时。

不幸的是,这个雄心勃勃的计划被证明是一个比预期更大的问题。 必须移植大部分代码库以支持核心和重要扩展的 Unicode,这被证明是乏味和棘手的。 这减慢了语言中其他功能的开发速度,使许多 PHP 开发人员在此过程中感到沮丧。 出现了额外的障碍,导致对开发原生 Unicode 支持的兴趣降低,最终导致该项目被放弃。

由于书籍和文章等资源是为 PHP 6 及其 Unicode 支持编写的,因此新版本将重命名为 PHP 7 以避免混淆。

无论如何,在悲伤的过去已经足够了,让我们看看 PHP 7 给聚会带来了什么。

性能之战,PHP 7 与 PHP 5

对于几乎所有更新,预计会有较小的性能升级。 然而,这一次 PHP 带来了对早期版本的显着改进,使得纯粹的性能成为 PHP 7 最吸引人的特性之一。 这是“PHPPNG”项目的一部分,该项目解决了 Zend 引擎本身的内部问题。

通过重构内部数据结构并以抽象语法树 (AST) 的形式在代码编译中添加中间步骤,结果是卓越的性能和更高效的内存分配。 这些数字本身看起来很有希望。 在真实世界应用程序上进行的基准测试表明,PHP 7 的平均速度是 PHP 5.6 的两倍,并且请求期间的内存消耗减少了 50%,这使得 PHP 7 成为 Facebook 的 HHVM JIT 编译器的强大竞争对手。 看看 Zend 的这张信息图,它描绘了一些常见的 CMS 和框架的性能。

图片:PHP 7 与 PHP 5 性能说明。

内存消耗的减少还允许较小的机器更好地处理请求,并有机会围绕 PHP 构建微服务。 内部变化,特别是 AST 实现,也为未来的优化开辟了可能性,从而进一步推动性能。 JIT 编译器的新内部实现正在考虑用于未来版本。

PHP 7 语法糖

PHP 7 带有新的语法特性。 虽然没有扩展语言本身的功能,但它们提供了一种更好或更简单的方法,使您的代码编写起来更加愉快和赏心悦目。

集团进口报关单

现在,我们可以将来自同一命名空间的类的导入声明分组到单个use行中。 这应该有助于以有意义的方式对齐声明,或者只是在文件中保存一些字节。

 use Framework\Module\Foo; use Framework\Module\Bar; use Framework\Module\Baz;

使用 PHP 7,我们可以使用:

 use Framework\Module\{Foo, Bar, Baz};

或者,如果您更喜欢多线样式:

 use Framework\Module{ Foo, Bar, Baz };

空合并运算符

这解决了 PHP 编程中的一个常见问题,我们希望从另一个变量中为一个变量分配一个值,如果后者实际已设置,或者为它提供不同的值。 当我们使用用户提供的输入时,它通常被使用。

PHP 7 之前的版本:

 if (isset($foo)) { $bar = $foo; } else { $bar = 'default'; // we would give $bar the value 'default' if $foo is NULL }

PHP 7 之后:

 $bar = $foo ?? 'default';

这也可以与许多变量链接:

 $bar = $foo ?? $baz ?? 'default';

宇宙飞船操作员

宇宙飞船运算符<=>允许在两个值之间进行三向比较,不仅指示它们是否相等,而且通过返回 1,0 或 -1 来指示不等式中哪个更大。

在这里,我们可以根据值的不同采取不同的操作:

 switch ($bar <=> $foo) { case 0: echo '$bar and $foo are equal'; case -1: echo '$foo is bigger'; case 1: echo '$bar is bigger'; }

比较的值可以是整数、浮点数、字符串甚至数组。 查看文档以了解如何将不同的值相互比较。 [https://wiki.php.net/rfc/combined-comparison-operator]

PHP 7 中的新功能

当然,PHP 7 也带来了新的令人兴奋的功能。

标量参数类型和返回类型提示

PHP 7 通过添加四种标量类型扩展了方法(类、接口和数组)中参数的先前类型声明; 整数 ( int )、浮点数 ( float )、布尔值 ( bool ) 和字符串 ( string ) 作为可能的参数类型。

此外,我们可以选择指定方法和函数返回的类型。 支持的类型是boolintfloatstringarraycallableClassInterface的 name 、 selfparent (用于类方法)

 class Calculator { // We declare that the parameters provided are of type integer public function addTwoInts(int $x, int $y): int { return $x + $y; // We also explicitly say that this method will return an integer } }

类型声明允许构建更健壮的应用程序并避免从函数传递和返回错误值。 其他好处包括静态代码分析器和 IDE,如果缺少 DocBlock,它们可以更好地了解代码库。

由于 PHP 是一种弱类型语言,参数和返回类型的某些值将根据上下文进行强制转换。 如果我们在一个声明参数类型为int的函数中传递值“3”,解释器将接受它作为整数并且不会抛出任何错误。 如果你不想要这个,你可以通过添加一个declare指令来启用strict mode

declare(strict_types=1);

这是在每个文件的基础上设置的,因为全局选项会将代码存储库划分为具有全局严格构建的那些和没有构建的那些,当我们组合来自两者的代码时会导致意外行为。

引擎异常

通过添加引擎异常,可以轻松捕获和处理可能导致脚本终止的致命错误。

诸如调用不存在的方法之类的错误不会终止脚本,而是会引发可由 try catch 块处理的异常,从而改进应用程序的错误处理。 这对于某些类型的应用程序、服务器和守护程序很重要,因为否则致命错误会要求它们重新启动。 PHPUnit 中的测试也应该变得更有用,因为致命错误会丢弃整个测试套件。 将在每个测试用例的基础上处理异常而不是错误。

PHP 7 根据可能遇到的错误类型添加了许多新的异常类。 为了保持版本之间的兼容性,添加了一个新的Throwable接口,该接口可以从引擎异常和用户异常中实现。 这是必要的,以避免引擎异常扩展基异常类,从而导致以前不存在的较旧的代码捕获异常。

在 PHP 7 之前,这会以致命错误终止脚本:

 try { thisFunctionDoesNotEvenExist(); } catch (\EngineException $e) { // Clean things up and log error echo $e->getMessage(); }

匿名类

匿名类是匿名函数的近亲,您可以在简单的短期实例中使用它们。 匿名类很容易创建和使用,就像普通对象一样。 这是文档中的一个示例。

PHP 7 之前

class MyLogger { public function log($msg) { print_r($msg . "\n"); } } $pusher->setLogger( new MyLogger() );

使用匿名类:

 $pusher->setLogger(new class { public function log($msg) { print_r($msg . "\n"); } });

匿名类在单元测试中很有用,尤其是在模拟测试对象和服务时。 通过创建一个提供我们想要模拟的接口的简单对象,这有助于我们避免大量模拟库和框架。

CSPRNG 函数

添加了两个用于生成加密安全字符串和整数的新函数。

 random_bytes(int $len);

返回一个长度$len的随机字符串。

 random_int(int $min, int $max);

返回$min$max之间的数字。

Unicode 代码点转义语法

与许多其他语言不同,在 PHP 7 之前,PHP 没有办法在字符串文字中转义 Unicode 代码点,. 此功能添加转义\u序列以使用其 UTF-8 代码点生成此类字符。 这比直接插入字符要好,可以更好地处理不可见字符以及具有相同图形表示但含义不同的字符。

echo "\u{1F602}"; // outputs ‚

请注意,这会使用\u序列破坏现有代码,因为它会改变行为。

发电机升级

PHP 中的生成器还有一些不错的附加功能。 现在,生成器有一个 return 语句,可用于允许它在迭代后输出最终值。 这可以用来检查生成器是否已正确执行,并允许调用生成器的代码适当地处理各种场景。

此外,生成器可以从其他生成器返回和生成表达式。 这使他们能够将复杂的操作划分为更简单的模块化单元。

 function genA() { yield 2; yield 3; yield 4; } function genB() { yield 1; yield from genA(); // 'genA' gets called here and iterated over yield 5; return 'success'; // This is a final result we can check later } foreach (genB() as $val) { echo "\n $val"; // This will output values 1 to 5 in order } $genB()->getReturn(); // This should return 'success' when there are no errors.

期望

期望是对assert()函数的增强,同时保持向后兼容性。 它们允许在生产代码中进行零成本断言,并提供在断言失败时抛出自定义异常的能力,这在开发过程中很有用。

assert()成为 PHP 7 中的一种语言结构。断言只能在开发和测试环境中用于调试目的。 为了配置它的行为,我们提供了两个新指令。

  • zend.assertions
    • 1 :生成并执行代码(开发模式)(默认值)
    • 0 :生成代码但在运行时跳转
    • -1 :不生成代码,使其零成本(生产模式)
  • assert.exception
    • 1 :断言失败时抛出,通过抛出作为异常提供的对象或在未提供异常时抛出新的AssertionError对象
    • 0 :使用或生成Throwable如上所述,但仅基于该对象生成警告而不是抛出它(与 PHP 5 行为兼容)

准备从 PHP 5 迁移到 PHP 7

主要版本的引入带来了更改/更新旧功能的机会,甚至在它们被认为太旧或已被弃用一段时间时将其删除。 此类更改可能会导致旧应用程序的兼容性中断。

这种版本飞跃引发的另一个问题是,您所依赖的重要库和框架可能尚未更新以支持最新版本。 PHP 团队已尝试使新更改尽可能向后兼容,并允许尽可能轻松地迁移到新版本。 更新和更新的应用程序应该更容易迁移到新版本,而旧应用程序可能必须决定收益是否超过成本,可能选择不更新。

大多数休息时间都很轻微,可以轻松缓解,而其他休息时间可能需要更多的努力和时间。 基本上,如果您在安装 PHP 7 之前在您的应用程序中出现了弃用警告,您可能会遇到错误,这些错误会破坏应用程序,直到修复为止。 你被警告了,对吧?

旧的 SAPI 和扩展

最重要的是,旧的和已弃用的 SAPI 像mysql扩展一样被删除(但你不应该首先使用它,对吧?)。 有关已删除的扩展和功能的完整列表,您可以在此处和此处查看此 RFC。

此外,其他 SAPI 正在移植到 PHP 7。

从 PHP 7 中删除了大量旧的 SAPI 和扩展。我们猜测它们不会被遗漏。

统一变量语法

此更新进行了一些更改,有利于变量结构的一致性。 这允许使用变量进行更高级的表达式,但在其他一些情况下会引入行为变化,如下所示。

 // old meaning // new meaning $$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz'] $foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz'] $foo->$bar['baz']() $foo->{$bar['baz']}() ($foo->$bar)['baz']() Foo::$bar['baz']() Foo::{$bar['baz']}() (Foo::$bar)['baz']()

这将破坏应用程序访问此类值的行为。 另一方面,你可以做一些像这样的整洁的东西:

 // Nested () foo()(); // Calls the return of foo() $foo->bar()(); // IIFE syntax like JavaScript (function() { // Function body })(); // Nested :: $foo::$bar::$baz

旧样式标签已删除

开始/结束标签<% ... %><%= ... %><script language="php"> ... </script>被删除并且不再有效。 用有效的替换它们应该很容易,但是你用它们做什么呢,怪人?

类、接口和特征的无效名称

由于添加了参数和返回类型等类、接口和特征,不再允许使用以下名称:

  • 布尔
  • 整数
  • 漂浮
  • 细绳
  • 空值
  • 真的
  • 错误的

这些会破坏使用它们的现有应用程序和库,但它们应该很容易修复。 此外,尽管它们不会导致任何错误并且有效,但不应使用以下内容,因为它们是为将来使用而保留的:

  • 资源
  • 目的
  • 混合
  • 数字

避免使用它们应该可以节省您将来更改它们的工作。

有关会破坏兼容性的更改的完整列表,请查看此文档。

您也可以使用 php7cc,它会检查您的代码,并可以检测到您迁移到 PHP 7 后可能出现的任何潜在问题。当然,没有比安装 PHP 7 并亲自查看更好的方法了。

潜在的 PHP 7 兼容性问题

PHP 7 基础架构兼容性

许多托管服务已开始添加对 PHP 7 的支持。这对共享托管服务提供商来说是个好消息,因为性能提升将使他们能够增加硬件上的客户端网站数量,从而降低运营费用并提高利润。 至于客户本身,他们不应该期望在这些条件下获得太多提升,但公平地说,共享主机无论如何都不是一个以性能为导向的选择。

另一方面,提供虚拟专用服务器或专用服务器的服务将获得这种性能提升的全部好处。 一些 PaaS 服务(如 Heroku)很早就支持 PHP 7,但其他服务(如 AWS Beanstalk 和 Oracle 的 OpenShift)则落后。 检查您的 PaaS 提供商的网站,了解是否已经支持 PHP 7,或者是否在不久的将来提供支持。

当然,IaaS 提供商允许您控制硬件并安装 PHP 7(如果您更喜欢的话,也可以编译)。 PHP 7 包已经可用于主要的 IaaS 环境。

PHP 7 软件兼容性

除了基础架构兼容性之外,您还需要注意潜在的软件兼容性问题。 WordPress、Joomla 和 Drupal 等流行的内容管理系统在其最新版本中增加了对 PHP 7 的支持。 Symfony 和 Laravel 等主要框架也得到了全面支持。

但是,现在是时候提个醒了。 这种支持不会扩展到以附加组件、插件、包或您的 CMS 或框架调用它们的任何形式的第三方代码。 他们可能会遇到兼容性问题,您有责任确保一切都为 PHP 7 做好准备。

对于活动的、维护的存储库,这应该不是问题。 但是,缺乏 PHP 7 支持的较旧和未维护的存储库可能会使您的整个应用程序无法使用。

PHP 的未来

PHP 7 的发布删除了旧的和过时的代码,并为未来的新功能和性能升级铺平了道路。 此外,预计 PHP 将很快获得额外的性能优化。 尽管与过去的版本存在一些兼容性问题,但大多数问题都很容易解决。

库和框架现在正在将其代码迁移到 PHP 7,从而提供最新版本。 我鼓励您尝试一下并亲自查看结果。 也许您的应用程序已经兼容并等待使用 PHP 7 并从中受益。

相关: PHP 开发人员最常犯的 10 个错误列表