独立开发者Kane

Game designers and technology enthusiasts


现代PHP特性解析 – PHP 8.0 及 8.1

本教程将向大家展示笔者在什么场景下会使用 PHP 8 新特性,并借此带领大家熟悉这些新特性。接下来我们一起来了解 PHP 8.0 主要的一些新特性。

构造器属性提升

这是我最常用的 8.0 特性之一,使用此特性笔者节约了许多敲打键盘的时间。参考例子:

// PHP 8.0 之前
class Client
{
    private string $url;
 
    public function __construct(string $url)
    {
        $this->url = $url;
    }
}
// PHP 8.0
class Client
{
    public function __construct(
        private string $url,
    ) {}
}

现在我们可以直接在构造器 constructor 中直接声明类的属性。现在我经常使用这一特性,它简化类中属性声明并进行初始化赋值的操作。

联合类型

另一个有意思的新特性是联合类型(Union Types)。这个特性用在类型提示或返回值有一个或者多个类型时。

// PHP 8.0 之前
class PostService
{
    public function all(): mixed
    {
        if (! Auth::check()) {
            return [];
        }
 
        return Post::query()->get();
    }
}

// PHP 8.0
class PostService
{
    public function all(): array|Collection
    {
        if (! Auth::check()) {
            return [];
        }
 
        return Post::query()->get();
    }
}

这一新特性有助于静态分析理解我们代码,也有助于我们理解自己的代码 – 即使只是粗略一瞥。我们看到 all 方法返回的要么是数组要么是collection, 这意味着我们的代码将会更具可预测性。

命名参数

命名参数能使我们的编码更加声明式 – 比如,不用再去猜测所调用函数的第三个参数指的什么。举例:

// PHP 8.0 之前
class ProcessImage
{
    public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
    {
        // logic for handling image processing
    }
}
 
ProcessImage::handle('/path/to/image.jpg', 500, 300, 'jpg', 100, 5);

// PHP 8.0
class ProcessImage
{
    public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
    {
        // logic for handling image processing
    }
}
 
ProcessImage::handle(
    path: '/path/to/image.jpg',
    height: 500,
    width: 300,
    type: 'jpg',
    quality: 100,
    compression: 5,
);

如上例所示 – height 和 width 参数如果搞混了,可能会出现预期之外的结果。如果像上例这样,类和实例彼此紧挨着,那问题不大。但是如果这个方法来自于你从其他地方引入安装的一个包里,而这个包文档可能也没做好 – 这种情况下,使用命名参数将有助于包的使用者理解参数的顺序。不过使用该特性需要谨慎,因为包作者倾向于频繁地修改参数名称,并且通常不会将其当作破坏性修改。

Match 表达式

以前当有多个 case 时,我们会使用大段的 switch 语句。老实说,看起来并不美观也不直观。Match 表达式对此作了改进。

// PHP 8.0 之前
switch (string $method) {
    case 'GET':
        $method = 'GET';
        break;
    case 'POST':
        $method = 'POST';
        break;
    default:
        throw new Exception("$method is not supported yet.");
}

// PHP 8.0
match (string $method) {
    'GET' => $method = 'GET',
    'POST' => $method = 'POST',
    default => throw new Exception(
        message: "$method is not supported yet.",
    ),
};

Match 语句使用了更加精简的语法,且更具可读性。性能上的改进有多少我不太清楚,不过对代码的编写确实更加友好。

在实例中使用 ::class

过去,如果你像传入类字符串到方法中,你必须使用类似于 get_class 这样的东西,看起来似乎没有意义。系统在调用时已经知道这个类了,因为你已经自动加载或创建了实例。如下例:

// PHP 8.0 之前
$commandBus->dispatch(get_class($event), $payload);

// PHP 8.0
$commandBus->dispatch(
    event: $event::class,
    payload: $payload,
);

在所有这些新特性中,这可能不是很重要的一个,不过它是在需要用到时我肯定会使用的一个。

无捕获的 catch 块

有时候,你并不需要获取抛出的异常,虽然并不一定常见。

// PHP 8.0 之前
try {
    $response = $this->sendRequest();
} catch (RequestException $exception) {
    Log::error('API request failed to send.');
}
// PHP 8.0
try {
    $response = $this->sendRequest();
} catch (RequestException) {
    Log::error('API request failed to send.');
}

像上例中,我们并不需要使用到 exception 实例对象。不过在实际开发中,我经常还是会需要用到的。

以上是 PHP 8.0 的一些新特性。 接下来,我们来看看 PHP 8.1 给我们带来了哪些新特性。

枚举 Enum

枚举是我最喜欢的 PHP 8.1 新特性之一。现在我可以把那些永远不会改变的数据存到枚举中,而不用再存入到一张永远不会修改的表格中。

// PHP 8.1 之前
class Method
{
    public const GET = 'GET';
    public const POST = 'POST';
    public const PUT = 'PUT';
    public const PATCH = 'PATCH';
    public const DELETE = 'DELETE';
}

// PHP 8.1
enum Method: string
{
    case GET = 'GET';
    case POST = 'POST';
    case PUT = 'PUT';
    case PATCH = 'PATCH';
    case DELETE = 'DELETE';
}

以上展示了语法上的不同,使用上又有什么差异呢?下例是我在 API 集成中通常会用到的一个 trait:

// PHP 8.1 之前
trait SendsRequests
{
    public function send(string $method, string $uri, array $options = []): Response
    {
        if (! in_array($method, ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])) {
            throw new InvalidArgumentException(
                message: "Method [$method] is not supported.",
            );
        }
 
        return $this->buildRequest()->send(
            method: $method,
            uri: $uri,
            options: $options,
        );
    }
}

// PHP 8.1
trait SendsRequests
{
    public function send(Method $method, string $uri, array $options = []): Response
    {
        return $this->buildRequest()->send(
            method: $method->value,
            uri: $uri,
            options: $options,
        );
    }
}

它让我的方法可以通过类型透视准确把握传入的参数,并减少由于不支持类型抛出异常的可能性。如果我们想要扩展支持,我们只需要在Enum中添加新的 case 。

数组解包

以前我们通常是进行复制或者合并数组。现在我们可以使用这一新特性,就可以实现数组的解包。

// PHP 8.1 以前
final class CreateNewClient implements CreateNewClientContract
{
    public function handle(DataObjectContract $client, int $account): Model|Client
    {
        return Client::query()->create(
            attributes: array_merge(
                $client->toArray(),
                [
                    'account_id' => $account,
                ],
            ),
        );
    }
}

// PHP 8.1
final class CreateNewClient implements CreateNewClientContract
{
    public function handle(DataObjectContract $client, int $account): Model|Client
    {
        return Client::query()->create(
            attributes: [
                ...$client->toArray(),
                'account_id' => $account,
            ],
        );
    }
}

如你所见,代码量精简了许多。

 构造器中使用 New 关键字

// PHP 8.1 之前
class BuyerWorkflow
{
    public function __construct(
        private null|WorkflowStepContract $step = null
    ) {
        $this->step = new InitialBuyerStep();
    }
}
// PHP 8.1
class BuyerWorkflow
{
    public function __construct(
        private WorkflowStepContract $step = new InitialBuyerStep(),
    ) {}
}

在我看来,这一特性至少在代码上更加干净。在构造器上使用这一特性,我们不用再去担心会不会有传入null 值的可能问题 – 让类自己去处理这个问题。

只读属性

以前我需要将想要用 public 公开的属性改成 protected 或者 private – 这就是说我不得不因此为这个类中再添加getter 。

// PHP 8.1 之前
class Post
{
    public function __construct() {
        protected string $title,
        protected string $content,
    }
 
    public function getTitle(): string
    {
        return $this->title;
    }
 
    public function getContent(): string
    {
        return $this->content;
    }
}

// PHP 8.1
class Post
{
    public function __construct() {
        public readonly string $title,
        public readonly string $content,
    }
}


About Me

I am a seasoned game development programmer with 16 years of extensive work experience. I am passionate about game development and dedicated to creating awe-inspiring gaming experiences. Through my technical skills and creativity, I constantly drive the progress of the gaming industry.

我是一名资深游戏开发工程师,拥有16年的丰富工作经验。我对游戏开发充满热情,并致力于创造令人惊叹的游戏体验。通过我的技术和创意,我不断推动游戏行业的发展。

Programming Languages: I am proficient in several programming languages, including C++, C#, Golang、Python, and more. I skillfully utilize these languages to implement core functionalities, optimize performance, and ensure game stability.

编程语言:我精通多种编程语言,包括C++、C#、Golang、Python等。我熟练运用这些语言来实现游戏的核心功能,优化性能,并确保游戏的稳定性。

Game Engines: I have extensive experience with multiple game engines, including Unity and Unreal Engine. I leverage the powerful features of these engines to create visually stunning game worlds.

游戏引擎:我在多个游戏引擎上有丰富的经验,包括Unity和Unreal Engine。我能够利用这些引擎的强大功能,打造出精美绝伦的游戏世界。

2d ultimate bundle Advanced Culling System 2 Animated 2D Monsters ca Dark Pixel UI Fantazia final cut pro 10.6.10 fps Gaia Pro 2023 game ai gena pro hackmssql ip欺诈 ip解析 Low Poly Shooter Pack mod nms Parallels desktop Parallels desktop 19 for mac 免费激活方法 php php8.0 php8.1 Post Apocalyptic Destroyed Buildings projectzomboid scum soft-nms terrains unity unity asset vpn vpn搭建 vps windows11免费激活 xmind yolov5 yolov5+ca+soft-nms 人渣弹幕mod 信息对称平台 僵尸毁灭工程弹幕mod 弹幕游戏 有点用的网站 梯子搭建 激活码 科学上网 黑进服务器