PHP 8.4有何新特性和改进?

what-is-new-in-php-8-4-1024x512-1

空气中弥漫着秋冬的味道,所以是时候推出 PHP 的新版本了,PHP 是一种服务器端脚本语言,为我们最喜欢的内容管理系统 WordPress 提供动力。在 11 月 21 日发布 8.4 GA 版本之前,PHP 开发人员发布了许多新代码库的早期版本,包括 8 月份功能冻结后的一些候选发布版本。

除了新功能、改进和弃用之外,我们预计 2024 年的 PHP 发布周期也会有所调整,所有当前支持版本的安全发布结束时间将同步到年底,而不是其 GA 生日。

更重要的是,支持时间延长了一年,这意味着 PHP 8.4 可以安全地使用到 2028 年(其中两年是安全和错误修复,两年只是安全修复)。

虽然您可以花更多时间使用 PHP 8.4,但您可能现在就想了解该版本的新功能。

PHP 8.4新功能和改进

与 8.4 中的一些新增功能相比,去年发布的 PHP 8.3 中的新功能显得有些低调:

  1. 属性钩子
  2. 非对称可见性
  3. 不带括号的链式调用 new
  4. 查找数组项的新函数
  5. HTML5 解析
  6. 多字节修剪函数

属性钩子

属性钩子为处理PHP 面向对象编程(OOP)中的 “getters ”和 “setters ”带来了全新的方法,使您可以简化类文件的结构。

下面的简单类中包含了$size$flavor 属性,作为属性钩子可以替代的示例。它们具有私有可见性,以保护它们不被结果对象外部直接访问。这就是为什么公共 getter 和 setter 方法会调解对属性的访问:

class Coffee
{
private string $size;
private string $flavor;
public function __construct(string $size, string $flavor) {
$this->size   = $size;
$this->flavor = $flavor;
}
// "Setting" coffee size and flavor
public function setSize(string $size): void {
$this->size = $size;
}
public function setFlavor(string $flavor): void {
$this->flavor = $flavor;
}
// "Getting" coffee size and flavor
public function getSize(): string {
return $this->size;
}
public function getFlavor(): string {
return $this->flavor;
}
} // End of class
// Make some coffee
$coffee = new Coffee('Small', 'Pumpkin Spice');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Small Pumpkin Spice"
// Change order
$coffee->setSize('Grande');
$coffee->setFlavor('Mocha');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Grande Mocha"

或者,也许你的类有许多属性,与其编写许多 getter 和 setter 方法,不如使用 PHP 的 _get_set 神奇方法。你甚至可以像下面这段摘录一样,用一个有点乱的 switch 语句来处理事情。

// __set magic method example
public function __set(string $key, $value): void 
switch ($key) {
case 'size':
$this->size = $value;
break;
case 'flavor':
$this->flavor = $value;
break;
default:
throw new InvalidArgumentException('Invalid input');
}
}
// Later, we can change the coffee order like this:
$coffee->size = 'Grande';
$coffee->flavor = 'Mocha';

无论您选择哪种方法,类中的属性越多,用于操作这些属性的代码就离类文件顶部的定义越远。此外,某些 _get_set 魔法方法的实现可能会意外地提供对对象中的私有或受保护属性的访问权限,而这些属性本不是你打算公开的。

新的属性钩子功能将获取器和设置器功能与属性本身捆绑在一起。在下面的属性钩子示例中,你会注意到 Coffee 类的 $size$flavor 属性现在是公开的。不过,我们也为 set 钩子添加了一些基本验证,使其有别于直接赋值。

// Property definitions at the top of our Coffee class
class Coffee
{
public string $flavor {
set(string $value) {
if (strlen($value) > 16) throw new InvalidArgumentException('Input is too long');
$this->flavor = $value;
}
}
public string $size {
set(string $value) {
if (! in_array($value, array(‘Small’, ‘Grande’))) throw new InvalidArgumentException('Not a valid size');
$this->size = $value;
}
}
// Rest of the Coffee class
}
// Define a coffee
$coffee = new Coffee();
$coffee->size = 'Grande';
$coffee->flavor = 'Pumpkin spice';

同样,如下所示,get 钩子可以将功能打包到看似普通的对象属性引用中。

// Simplified Coffee class
class Coffee
{
public string $flavor {
get { 
return $this->flavor . ' Spice';
}
}
}
// Create a flavor 
$coffee = new Coffee();
$coffee->flavor = 'Pumpkin'; // Stores the value "Pumpkin"
print $coffee->flavor;       // Prints "Pumpkin Spice"

与 PHP 魔术方法不同,属性钩子可以在接口和抽象类中使用。界面示例:

interface Coffee
{
public string $size { get; set; }
public string $flavor { get; set; }
}

不对称可见性

我们前面提到的公开可见的 getter 和 setter 方法是访问类中私有和受保护属性的传统方法。

PHP 8.4 的一个有趣特性是,根据访问的上下文,属性可以有不同级别的可见性。因此,一个属性在被读取时可能是公开的,但在被设置时可能是私有或受保护的。

看看这个:

class Coffee
{
public private(set) string $flavor = 'Pumpkin Spice';
}
$coffee = new Coffee();
print $coffee->flavor;     // Prints "Pumpkin Spice"
$coffee->flavor = 'Mocha';  // Error (visibility)

在上文中,该类的 $flavor 属性是公开的,但在设置上下文中除外。这已经很简单了,但非对称可见性甚至还有一点捷径:

class Coffee
{
// public is assumed when the context is not setting
private(set) string $flavor = 'Pumpkin Spice';
}

您可以结合使用属性钩子和非对称可见性,从而在处理各种可见性的对象属性时获得极大的灵活性。

不带括号的链式调用 new

说到速记,以前调用 new 和链式方法时需要将其调用放在括号中,就像下面这样:

$coffee = (new Coffee())->getFlavor()->getSize();

PHP 8.4 允许这样做:

$coffee = new Coffee()->getFlavor()->getSize();

这看似是一个微小的改动,但去掉两个括号后,阅读和调试都变得更加容易。

查找数组项的新函数

PHP 8.4 引入了函数 array_find(),它可以搜索数组元素中与回调函数中表达的条件相匹配的成员。该函数返回符合回调测试的第一个元素的值。

新版本还包括其他三个相关函数:

  • array_find_key() :与 array_find() 类似,但返回值是匹配元素的 key,而不是元素本身的值。
  • array_all():如果被测试数组中的每个元素符合回调测试,则返回 true
  • array_any():如果数组中至少有一个元素符合回调测试,则返回true

请注意,后两个函数返回的是布尔指标,而不是数组键或内容。

下面是一些快速示例:

$array = [
'a' => 'Mocha',
'b' => 'Caramel',
'c' => 'Maple',
'd' => 'Pumpkin'
];
// Find the first flavor name that is 5 characters long
var_dump(array_find($array, function (string $value) {
return strlen($value) == 5;
})); // Returns “Mocha,” even though “Maple” is the same length 
// Find the array key for the first flavor with a name longer than 5 characters.
var_dump(array_find_key($array, function (string $value) {
return strlen($value) > 5;
})); // Returns “b”
// Check to see if any flavor name is less than 5 characters long
var_dump(array_any($array, function (string $value) {
return strlen($value) < 5;
})); // Returns false
// Check to see if all flavor names are shorter than 8 characters
var_dump(array_all($array, function (string $value) {
return strlen($value) < 8;
})); // Returns true

HTML5解析

HTM5 是现代网页结构的事实标准,但 PHP 的文档对象模型 (DOM) 解析技术却在 HTML 4.01 时停滞不前。

PHP 8.4 并没有升级现有的 DOMDocument 类(该类适用于较旧的 HTML 标准),而是提供了一个新的 DomHTMLDocument 类,该类已为 HTM5 做好准备。

您可以像这样导入 HTML5 页面的内容:

$document = DomHTMLDocument::createFromString($html)

除了上述 createFromString($html) 构造函数外,该类还支持 createFromFile($path)createEmpty() 函数。

新的解析器能识别诸如 mainarticlesection 等语义 HTML5 标记,这些标记现在已为大多数人所熟悉。

多字节修剪函数

PHP 8.4 中的另一个新增功能似乎是姗姗来迟,那就是在修剪函数中支持多字节:

  • mb_trim()
  • mb_ltrim()
  • mb_rtrim()

与使用已久的 PHP trim() 函数一样,mb_trim 会从可能包含多字节字符的字符串两端删除空白和一些特殊字符(如换行符)。其他函数要么修剪字符串的左端,要么修剪字符串的右端。

PHP 8.4中的弃用

PHP 的每个版本都会带来一份功能和函数清单(有些非常晦涩难懂),这些功能和函数会被标记为最终将从平台中删除。PHP 8.4 中一个比较引人注目的弃用是非 Cookie 会话跟踪。

GET/POST会话的弃用

虽然 Cookie 通常是跟踪用户会话的首选方法,但 PHP 支持在 GET/POST 参数中固定会话 ID 数据。要通过 URL 中的参数启用会话跟踪,需要禁用 PHP 设置 session.use_only_cookies,并启用设置 session.use_trans_sid

在 PHP 8.4 中,这些设置的任何一种状态都会触发网站日志中可能出现的弃用警告。PHP 9 发布后,这些设置将不再可用。

PHP 8.4中的其他弃用(和移除)

下面列出了 PHP 8.4 开发团队计划弃用的功能。(其中一些功能包含指向更多信息的链接)

  • 正式废弃的 DOMDocumentDOMEntity 属性。
  • 移除了 DOMImplementation::getFeature($feature,$version)
  • 弃用 DOM_PHP_ERR 常量。
  • 弃用 unserialize 中的“S”标记。
  • 弃用 session.sid_lengthsession.sid_bits_per_character
  • 弃用 SplFixedArray::__wakeup()
  • 废止使用字符串方法名的 xml_set_object()xml_set_*_handler()
  • 禁止向 dba_key_split() 传递 null 和 false。
  • 禁止为 ext/hash 函数的选项传递错误的数据类型。
  • 停用常量 SUNFUNCS_RET_STRINGSUNFUNCS_RET_DOUBLESUNFUNCS_RET_TIMESTAMP
  • 废止专有的 CSV 转义机制。
  • 弃用 E_STRICT 常量。
  • 弃用 strtok()
  • 停用从用户输出处理程序返回非字符串值的功能。
  • 取消在用户输出处理程序中产生输出。
  • 取消使用数组 $datafile_put_contents()
  • 停用 mysqli_ping()mysqli::ping()
  • 弃用 mysqli_refresh()
  • 停用 mysqli_kill()
  • 停用 mysqli_store_result() 的第二个参数。
  • 停用 lcg_value()函数
  • 停用 uniqid()
  • 停用 md5(), sha1(), md5_file()sha1 _ file()
  • 不再向 trigger_error() 传递 E_USER_ERROR 消息。
  • 不再使用单个下划线 (“_”) 作为类名。
  • 废弃将 SOAP_FUNCTIONS_ALL 常量传递给 SoapServer::addFunction() 的做法。

PHP 的每一个版本都会带来一份功能和函数(有些非常晦涩难懂)的废弃列表,这些功能和函数将被标记为最终从平台中删除。

小结

PHP 8.4 带来了一些有趣的变化。相信一些评测机构或者开发人员将很快测试该版本,比如 PHP 基准测试-对各种基于 PHP 的内容管理系统进行的测试。

我们也很想知道开发人员何时开始将 PHP 8.4 的一些新特性(尤其是属性钩子)应用到他们的项目中。

您最喜欢 PHP 8.4 的哪些功能?请在评论中分享您的想法!

评论留言