版本 8 改变
正如我们所看到的,PHP 在过去几年中经历了非同寻常的发展势头。虽然我们认为第 7 版是该语言的真正重生,但第 8 版证明这仅仅是个开始。以下是一些主要的新特性,它们将帮助你编写简洁明了的代码,并进一步推动我们在这些章节中所看到的简洁代码原则。
匹配语法
match 语法是经典 switch/case
语法的浓缩版。它不应到处使用,因为它很快就会变得难以阅读。但是,如果你选择在某些地方少用它,你的代码就会在瞬间变得更加清晰。下面是一个匹配语法的示例:
$foo = match($var) {
‹value 1› => Bar::myMethod1(),
‹value 2› => Bar::myMethod2(),
};
php
其工作方式与 switch 相同。不过,请注意代码长度的不同以及可读性的提高。你也可以立即看到这种语法的局限性:如果每个 case 需要执行的语句超过一条,那么它就完全不适应,而且你可能会得到一些无法阅读的代码。因此,最好坚持使用更经典的 switch/case
块。
命名参数
如果你经常使用其它语言,可能会对 PHP 的这种演变感到熟悉。从 8.0 版开始,我们可以按照自己想要的顺序将参数传递给方法。具体做法是在参数值之前指定参数的名称。
$this->myMethodCall(needle: 'Bar', enabled: true);
php
我们很快就能看到两种有用的情况:
-
首先,我们有时会遇到带有大量可选参数的方法,而这些参数已经定义了默认值。有时,我们想更改一个参数的值,而这个参数可能是列表中的第 5 个或第 11 个。这就需要重写前面所有参数的默认值,过程非常漫长。这显然并不理想,但使用命名参数可以解决这个问题。你只需指定要给出的参数名称及其值,就大功告成了。
-
第二种情况是为方法调用添加清晰度,即使它没有可选参数。如果你的方法有很多参数,你可能会考虑使用命名参数来明确显示你要发送给方法的参数。不过,如果是这种情况,要使代码更可读,真正的解决办法是重构代码,这样就不需要向方法传递那么多参数了。这可以通过将方法拆分成几个方法,或创建值对象(VO)来实现。这些对象只包含简单的属性,没有其它方法或逻辑,用于在代码中将数据从一点传输到另一点。这样可以避免无穷无尽的参数的烦恼,还可以通过强类型属性添加一层验证,并为所携带的数据添加一些上下文。
只读类和属性
下面是一个介绍只读类和只读属性的小谜语。在 PHP 类中,如何确保一个属性只能被赋值一次—也就是说,只能赋值一次,以后所有赋值的尝试都将失败?请注意,这也适用于类本身的赋值。这意味着,在调用情况下抛出异常的突变器是行不通的,因为我们无法百分之百地确定开发人员会在类的作用域内通过突变器。
实际上,答案很简单:这是不可能的。如果你使用的是 PHP 8.0 或更早版本,这是不可能的。PHP 8.1 为我们带来了解决这个问题的本地解决方案:只读属性。通过使用只读关键字声明类属性,变量只能被赋值一次,与上下文无关。这在定义和使用 DTO 和 VO 时尤其有用。通过限制对这些属性突变的访问,使用这些属性的开发人员必须仔细考虑对象的使用。如果他们想进行更改,就必须创建一个新对象。原始对象不能被修改,这可以保证代码具有更好的稳定性和健壮性。下面是只读属性的声明方式:
<?php
namespace App\Model;
class MyValueObject
{
protected readonly string $foo;
public function __construct(string $foo)
{
$this->foo = $foo; // First assignment, all good
// Any further assignment of $this->foo will result in a fatal error
}
}
php
自 PHP 8.2 起,甚至可以通过在 class 关键字前放置该修饰符来将整个类声明为只读,这样就不必在类中的每个属性上都使用该关键字。这样做还有一个很大的好处:声明为只读的类将无法动态声明新属性。尽管这种行为已被淘汰,而且应该不惜一切代价避免(动态声明类属性在调试、稳定性和代码复杂性方面都是一场噩梦),但在 PHP 9.0 及以下版本中仍然可以这样做。如果你使用的不是 PHP 9.0,将类声明为只读将保护你免受这种行为的影响。
这对 PHP 开发人员来说是一大进步。事实上,多亏了这一特性,我们将需要再次严格遵守与对象及其属性交互的方式。
将资源迁移到适当的类
根据你使用 PHP 进行开发的时间长短,你应该或多或少熟悉我们所说的资源。资源是一种特殊类型的变量,代表对外部资源的引用。这听起来可能有点含糊,但实际上非常琐碎。例如,资源可以是以下内容:
-
打开的文件
-
数据库连接
-
cURL 调用
-
与 LDAP 目录的连接(通常是管理公司用户帐户的一种方法)
-
GD 图像处理扩展的字体
几十年来,这种做法一直行之有效,但我们知道,资源一词过于笼统,尤其是在类和对象在现代代码中占主导地位的情况下,已经不再适用。为什么这些资源不是像其它对象一样简单的对象呢?没有特别的理由(至少目前没有)来证明这一点。这就是为什么 PHP 8.0 开始了漫长但必要的迁移,将旧的资源转换为成熟的类。这样做更有意义。
资源的使用很复杂。要调试和了解它们的工作原理和内部状态非常复杂。只有处理资源的特殊函数才能调用它们。对于开发人员来说,这是向前迈出的一大步,他们将拥有更多的控制权和更多的工具来提高开发的严谨性和代码的健壮性。
这种变化是随着新版本 PHP 的发布而进行的。例如,PHP 8.0 就包含了资源迁移功能,如下所示:
-
GD, for the manipulation of images
-
cURL
-
OpenSSL
-
XML
-
Sockets
相反,PHP 8.1 嵌入了以下资源的迁移:
-
GD fonts
-
FTP
-
IMAP
-
finfo, for file management
-
Pspell, for spell checking
-
LDAP
-
PostgreSQL
接下来的 PHP 版本也将继续这样做—最终取消使用标准 PHP 库中的资源。此外,PHP 的核心开发人员从 PHP 8.1 开始就决定在命名空间下创建其中的一些类。我们可以看到,通过这样的行动和发展,PHP 语言在经历了漫长的拖延之后正在迎头赶上,并尽一切努力恢复其声誉。PHP 清楚地表明:这种语言将继续存在下去。
保护你的敏感参数不被泄露
如果你已经使用 PHP 进行了一段时间的开发,你肯定知道有许多方法可以显示变量或函数参数的内容。尤其是 var_dump
和 print_r
。还有其它可以显示参数及其值的情况:显示堆栈跟踪(或调用堆栈)。这种情况可以通过手动调用 debug_print_backtrace
等方法来实现,也可以在抛出异常时经常出现。无论如何,如果在变量或调用堆栈中的某个地方包含了敏感信息以及方法调用的参数,这就会产生问题。你可能认为这种情况只会发生在开发环境中,但这是错误的。你很有可能将异常信息写入服务器上的错误日志中。这可能会导致敏感信息显示在日志中。这显然是不可取的。敏感信息不应以明文写入任何地方。此外,尽管这是一个错误,但应用程序日志的安全性往往不如数据库等。此外,项目的许多开发人员(即使不是全部)都有很大可能访问这些日志来调试程序。威胁并不总是来自外部。
幸运的是,PHP 8.2 包含了一个新属性来解决这个问题。事实上,在任何函数参数前标明 #[SensitiveParameter]
属性都是可行的。这将告诉 PHP 不要在 var_dump
、堆栈跟踪等中显示参数值。巧妙地放置这个属性,就能确保不会在错误信息中泄露敏感值。事实上,网站直接在前端显示服务器错误的情况并不少见。这种做法显然应该尽快禁止,但至少有助于限制损失。让我们看看如何使用这个新属性:
<?php
namespace App\Controller;
class SecurityController
{
public function authenticate(string $username,#[SensitiveParameter] string $password)
{
// In case of any exception occurring or var_dump being called in here, the value of $password will be hidden in the different outputs
}
}
php
在 PHP 的内部运行中,该属性将用一个 SensitiveParameterValue
类型的对象替换参数,从而隐藏参数的真实值。参数将在输出中显示和存在,但其值将被隐藏。在敏感方法参数中添加此属性是一种巧妙且受欢迎的方法,可为代码增添严谨性,使其更能抵御攻击。
总结
这一点再怎么重复也不为过: PHP 正在以最美丽的方式发展,并在网络世界中迎头赶上其竞争对手。PHP 语言倾听社区和开发人员的心声,为他们提供所需的工具,以最可行的方式解决现代问题。
我们已经走过了漫长的道路,以前的语言允许一切,但对于当今网络应用所面临的挑战来说却非常(过于)宽松。尽管旨在用前端语言取代服务器语言的前端框架和技术(如 JavaScript 的 Node.js)层出不穷,但 PHP 并没有什么可羞愧的。其令人印象深刻的性能、发展速度以及多年来建立起来的声誉都表明,PHP 仍有光明的未来。
正如我们所看到的那样,虽然简洁代码是一种思想境界,在某种程度上也是一种哲学,但这种语言的原生解决方案正在大量涌现,以帮助我们尽可能好地应用它们。更妙的是,PHP 带来的这些特性让我们看到了最初不一定能想到的新的可能性,从而使我们的代码具有健壮性、可维护性和长期可行性。试想一下命名参数、只读类和属性、严格类型,或者本章最后一个主题:保护敏感参数不泄漏到应用程序日志和异常消息中。
说了这么多,是时候进入正题了。我们将从一些工具开始,这些工具可以让你以数字形式快速了解代码的质量。有了这类指标,你就能知道自己将对代码做出哪些改进,或者是否真的到了该采取行动的时候,因为随着开发的进行,代码质量正在下降。下一章将重点介绍 PHP 语言专用的代码质量工具。