骑驴找蚂蚁

全干工程师

PHP 8_5

PHP 8.5新特性体验

PHP 官方在11有20号正式发布了8.5版本,虽然没有异步的这种大特性。但是还是有不少新的值得体验的特性,下面我就说说这版的一些新特性。

Uri 扩展

这是一个新的uri类,是根据(RFC 3986)标准来实现的解析。相比以前使用parse_url函数,这个uri扩展就更符合面向对象方式了.

  • 旧版本
$components = parse_url('https://php.net/releases/8.4/en.php');

var_dump($components['host']);
// string(7) "php.net"
  • 新版本
use Uri\Rfc3986\Uri;

$uri = new Uri("https://example.com/path/to/resource?query=string#fragment");

var_dump($uri);

//class Uri\Rfc3986\Uri#1 (8) {
//  public $scheme =>
//  string(5) "https"
//  public $username =>
//  NULL
//  public $password =>
//  NULL
//  public $host =>
//  string(11) "example.com"
//  public $port =>
//  NULL
//  public $path =>
//  string(17) "/path/to/resource"
//  public $query =>
//  string(12) "query=string"
//  public $fragment =>
//  string(8) "fragment"
//}

管道操作符

这个应该是重量级的更新了,这个操作符其实和bash差不多。不过我感觉PHP这个管道符太难打了,也有点丑😂,还有就是感觉也不是特别的简便。来看看下面官方给得例子。

  • 旧版本
$title = ' PHP 8.5 Released ';

$slug = strtolower(
    str_replace('.', '',
        str_replace(' ', '-',
            trim($title)
        )
    )
);

var_dump($slug);
// string(15) "php-85-released"
  • 新版本

$title = ' PHP 8.5 Released ';

$slug = $title
    |> trim(...)
    |> (fn($str) => str_replace(' ', '-', $str))
    |> (fn($str) => str_replace('.', '', $str))
    |> strtolower(...);

var_dump($slug);
// string(15) "php-85-released"

不知道你们发现没有,像str_replace这种多个参数的函数,它是需要一个匿名函数来包装下的。虽然老版本嵌套写起来不直观,这个写起也不是那么舒服😂。

克隆

现在可以通过向 clone() 函数传递关联数组,在对象克隆过程中更新属性。这使得对只读类的“with-er”模式能够得到直接支持。

  • 旧版本
readonly class Color
{
    public function __construct(
        public int $red,
        public int $green,
        public int $blue,
        public int $alpha = 255,
    ) {}

    public function withAlpha(int $alpha): self
    {
        $values = get_object_vars($this);
        $values['alpha'] = $alpha;

        return new self(...$values);
    }
}

$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
  • 新版本
readonly class Color
{
    public function __construct(
        public int $red,
        public int $green,
        public int $blue,
        public int $alpha = 255,
    ) {}

    public function withAlpha(int $alpha): self
    {
        return clone($this, [
            'alpha' => $alpha,
        ]);
    }
}

$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
# 如果这个类不是只读类你还可以这样

$secBlus = clone($blue, [
    'red' => 0,
    'green' => 0,
    'blue' => 255,
]);

var_dump($secBlus);

//class Color#3 (4) {
//  public int $red =>
//  int(0)
//  public int $green =>
//  int(0)
//  public int $blue =>
//  int(255)
//  public int $alpha =>
//  int(255)
//}

#[\NoDiscard] Attribute

添加过#[\NoDiscard]属性的函数, PHP会检查返回值是否已被使用,如果未被使用则发出警告。这有助于提高 API的安全性,尤其是在返回值至关重要但又容易被意外遗忘的情况下。我个人认为这个功能不是特别重要,在现在IDE的加持下这种函数IDE能识别出来,并会给出警告⚠️。


#[\NoDiscard]
function getPhpVersion(): string
{
    return 'PHP 8.5';
}

getPhpVersion();
// 发出下面的警告
// Warning: The return value of function getPhpVersion() should
// either be used or intentionally ignored by casting it as (void)

常量表达式中的闭包和一等可调用对象

现在可以在常量表达式中使用静态闭包和一等可调用对象。这包括属性参数、属性和参数的默认值以及常量。

  • 老版本

final class PostsController
{
    #[AccessControl(
        new Expression('request.user === post.getAuthor()'),
    )]
    public function update(
        Request $request,
        Post $post,
    ): Response {
        // ...
    }
}
  • 新版本

<?php
final class PostsController
{

    public function __construct(Closure $logger = static function (
        string $message,
    ): void {echo $message, "\n";})
    {
        // ...
        $logger("PostsController initialized.");
    }    


    public final $callback = static function (): string {
        // ...
        return "Hello, World! \n";
    };


    #[AccessControl(static function (
        Request $request,
        Post $post,
    ): bool {
        return $request->user === $post->getAuthor();
    })]
    public function update(
        Request $request,
        Post $post,
    ): Response {
        // ...
    }
}

echo (new PostsController()->callback)();

// PostsController initialized.
// Hello, World! 

持久化的cURL句柄

使用curl_share_init_persistent()创建句柄,PHP 请求结束后,该句柄不会被销毁。如果找到具有相同共享选项集的持久共享句柄,则会重用该句柄,从而避免每次都初始化cURL句柄的开销。

  • 旧版本
$sh = curl_share_init();
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);

$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);

curl_exec($ch);
  • 新版本

$sh = curl_share_init_persistent([
    CURL_LOCK_DATA_DNS,
    CURL_LOCK_DATA_CONNECT,
]);

$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);

// 现在可能会重用先前SAPI的连接
curl_exec($ch);

array_first() and array_last() 函数

array_first()array_last()函数分别返回数组的第一个值和最后一个值。如果数组为空,则返回 null(这样就很容易与??运算符组合使用)。说实话这两个函数在8.5才出来是真得太晚了。

<?php

$array = [
    "a" => 1,
    "b" => 2,
    "c" => 3,
];

echo array_first($array), "\n"; // Outputs: 1

echo array_last($array), "\n"; // Outputs: 3


推荐阅读

  1. https://wiki.php.net/rfc
  2. https://www.php.net/releases/8.5/en.php

相关程序解答推荐

留言