ZetCode

PHP 构造函数属性提升

最后修改于 2025 年 3 月 11 日

在本文中,我们将探讨 PHP 的构造函数属性提升,该特性在 PHP 8 中引入。此功能简化了类属性在构造函数中的声明和初始化。

构造函数属性提升 允许您直接在构造函数参数中声明和初始化类属性。这减少了模板代码,并使类定义更加简洁。

基本语法

语法结合了构造函数中的属性声明和初始化

main.php
<?php

declare(strict_types=1);

class Customer {
    public function __construct(
        private string $fullName,
        private int $customerId
    ) {}
}

在此示例中,$fullName$customerId 被声明为私有属性,并在构造函数中直接初始化,从而简化了电子商务系统 Customer 类的定义。

基本用法

此示例模拟了一个在线商店中带有提升属性的产品。

main.php
<?php

declare(strict_types=1);

class Product {
    public function __construct(
        private string $name,
        private float $price
    ) {}

    public function getDetails(): string {
        return "Product: $this->name, Price: $this->price";
    }
}

$product = new Product("Laptop", 999.99);
echo $product->getDetails();

在这里,Product 类使用构造函数属性提升来定义具有名称和价格的产品,这是在线商店中的典型属性。当使用诸如“Laptop”和 999.99 之类的值创建新的 Product 对象时,这些属性会自动分配。

这种方法消除了单独声明 $name$price 并在构造函数主体中赋值的需要,从而减少了重复代码。getDetails 方法演示了如何访问这些属性以显示产品信息,这使得该类既简洁又实用,可用于实际应用。

混合属性

此示例表示一个具有提升和非提升属性的订单。

main.php
<?php

declare(strict_types=1);

class Order {
    private string $orderDate;

    public function __construct(
        private int $orderId,
        private float $total,
        string $orderDate
    ) {
        $this->orderDate = $orderDate;
    }

    public function getSummary(): string {
        return "Order ID: $this->orderId, Total: $this->total, Date: $this->orderDate";
    }
}

$order = new Order(1001, 49.99, "2025-03-11");
echo $order->getSummary();

在这种情况下,Order 类模拟一个电子商务订单。$orderId$total 是提升的属性,直接在构造函数中声明和初始化,而 $orderDate 是手动分配的传统属性。

当某些属性需要额外处理(例如,验证或格式化)而无法单独通过提升处理时,这种混合是实用的。在这里,$orderDate 稍后可能会被验证为日期,但 $orderId$total 比较直接,从简洁的提升语法中受益。

默认值

此示例定义了一个访客用户,该用户具有登录系统的默认值。

main.php
<?php

declare(strict_types=1);

class User {
    public function __construct(
        private string $username = "guest",
        private int $loginCount = 0
    ) {}

    public function getProfile(): string {
        return "Username: $this->username, Logins: $this->loginCount";
    }
}

$guest = new User();
echo $guest->getProfile();

User 类使用具有默认值的构造函数属性提升来表示登录系统中的访客用户。如果未提供任何参数,$username 默认为“guest”,$loginCount 默认为 0,模拟未注册用户的配置文件。

这对于默认状态很常见的应用程序(例如,网站中的访客访问)非常有用。默认值减少了对其他工厂方法或使用回退值进行手动实例化的需求,从而使代码更简洁、更直观,便于测试或快速设置。

只读属性

此示例模拟了用于账单系统的不可变发票记录。

main.php
<?php

declare(strict_types=1);

class Invoice {
    public function __construct(
        private readonly string $invoiceNumber,
        private readonly float $amount
    ) {}

    public function getDetails(): string {
        return "Invoice: $this->invoiceNumber, Amount: $this->amount";
    }
}

$invoice = new Invoice("INV-001", 150.75);
echo $invoice->getDetails();

在此示例中,Invoice 类使用 readonly 与提升的属性来创建不可变的账单记录。一旦设置,$invoiceNumber$amount 无法修改,从而确保创建后的数据完整性。

这非常适合于发票发出后必须保持不变的财务系统。提升和 readonly 的组合简化了类定义,同时强制执行不可变性,从而降低了生产代码中意外数据更改的风险。

联合类型

此示例表示一个在 CRM 系统中具有灵活 ID 类型的客户。

main.php
<?php

declare(strict_types=1);

class Customer {
    public function __construct(
        private string|int $customerId,
        private string $contactName
    ) {}

    public function getContactInfo(): string {
        return "Customer ID: $this->customerId, Contact: $this->contactName";
    }
}

$customer = new Customer("CUST123", "Jane Smith");
echo $customer->getContactInfo();

Customer 类对 $customerId 使用联合类型(string|int),允许它接受数字或字符串 ID,这在具有旧版或混合数据源的 CRM 系统中很常见。$contactName 仍然是一个简单的字符串。

当与使用不同 ID 格式的外部系统集成时,这种灵活性很实用。构造函数属性提升保持了语法的简洁性,而联合类型确保了类可以处理各种输入,而无需额外的逻辑,从而增强了适应性。

可空属性

此示例模拟了一个在 HR 系统中具有可选部门的员工。

main.php
<?php

declare(strict_types=1);

class Employee {
    public function __construct(
        private string $name,
        private ?string $department = null
    ) {}

    public function getEmployeeInfo(): string {
        $dept = $this->department ?? "Not assigned";
        return "Employee: $this->name, Department: $dept";
    }
}

$employee = new Employee("Alice Brown");
echo $employee->getEmployeeInfo();

在此 HR 场景中,Employee 类使用可空的提升属性 $department,如果未指定,则默认为 null。这反映了员工可能尚未分配到部门的情况。

这对于跟踪人员配置的系统来说很实用,在创建时,某些数据可能是可选的。可空类型与提升相结合简化了类结构,并且 getEmployeeInfo 方法优雅地处理了 null 情况,提高了可用性。

多个构造函数

此示例为服务应用程序中的订阅提供了备用构造函数。

main.php
<?php

declare(strict_types=1);

class Subscription {
    public function __construct(
        private string $plan,
        private float $monthlyFee
    ) {}

    public static function createFreeTrial(): self {
        return new self("Trial", 0.0);
    }

    public function getPlanDetails(): string {
        return "Plan: $this->plan, Fee: $$this->monthlyFee/month";
    }
}

$freeSubscription = Subscription::createFreeTrial();
echo $freeSubscription->getPlanDetails();

Subscription 类对 $plan$monthlyFee 使用构造函数属性提升,并包括一个静态方法 createFreeTrial 来实例化免费试用订阅。这在基于订阅的服务(如流媒体或 SaaS 平台)中很常见。

这种方法允许灵活的对象创建——既可以通过构造函数创建自定义计划,也可以通过静态方法创建预定义的试用。它通过重用提升的属性来保持代码 DRY(不要重复自己),从而提高了可维护性和可读性。

继承

此示例扩展了一个用于会员系统中高级用户的基类。

main.php
<?php

declare(strict_types=1);

class Member {
    public function __construct(
        protected string $memberName,
        protected int $joinYear
    ) {}
}

class PremiumMember extends Member {
    public function __construct(
        string $memberName,
        int $joinYear,
        private string $membershipLevel
    ) {
        parent::__construct($memberName, $joinYear);
    }

    public function getMembershipInfo(): string {
        return "Name: $this->memberName, Joined: $this->joinYear, Level: $this->membershipLevel";
    }
}

$premium = new PremiumMember("Bob Wilson", 2023, "Gold");
echo $premium->getMembershipInfo();

Member 基类对 $memberName$joinYear 使用提升的属性,而 PremiumMember 扩展了它,添加了一个 $membershipLevel 属性。子类构造函数调用父类的构造函数以初始化继承的属性。

这对于会员系统来说很实用,其中一个基类定义了公共属性,而子类添加了特定功能。构造函数属性提升简化了基类,而继承确保了代码重用,从而使其能够有效地模拟分层会员级别。

带有验证的构造函数

此示例验证了预订系统中的预订详细信息。

main.php
<?php

declare(strict_types=1);

class Booking {
    public function __construct(
        private string $guestName,
        private int $roomNumber,
        private string $checkInDate
    ) {
        if ($roomNumber < 1 || $roomNumber > 500) {
            throw new ValueError("Room number must be between 1 and 500.");
        }
    }

    public function getBookingInfo(): string {
        return "Guest: $this->guestName, Room: $this->roomNumber, Check-in: $this->checkInDate";
    }
}

try {
    $booking = new Booking("Emma Lee", 501, "2025-04-01");
    echo $booking->getBookingInfo();
} catch (ValueError $e) {
    echo "Error: " . $e->getMessage();
}

Booking 类对 $guestName$roomNumber$checkInDate 使用提升的属性,并在构造函数中使用了验证逻辑,以确保房间号在有效范围内(1-500)。如果无效,则抛出 ValueError

这对于酒店或活动预订系统来说很实用,其中输入约束至关重要。将提升与验证相结合,使类保持简洁而稳健,减少了模板代码,同时在对象创建时强制执行业务规则。

try-catch 块演示了错误处理,确保系统可以优雅地处理无效输入(例如,房间 501),使其适合于数据完整性至关重要的生产环境。

具有数组属性的复杂对象

此示例模拟了一个包含项目数组的购物车。

main.php
<?php

declare(strict_types=1);

class ShoppingCart {
    public function __construct(
        private string $cartId,
        private array $items
    ) {}

    public function getCartSummary(): string {
        $itemCount = count($this->items);
        return "Cart ID: $this->cartId, Items: $itemCount";
    }
}

$cart = new ShoppingCart("CART-XYZ", ["Shirt", "Pants", "Shoes"]);
echo $cart->getCartSummary();

ShoppingCart 类使用提升的属性来定义 $cartId$items 数组,表示用户在电子商务应用程序中的购物车。构造函数在实例化时直接分配这些值。

这对于管理购物车非常有用,其中项目存储为数组,这是在线零售中的常见结构。提升功能使代码保持简洁,避免了单独的属性声明和赋值,同时支持复杂类型(如数组)。

getCartSummary 方法展示了如何使用提升的数组属性,提供了项目的简单计数。这种方法是可扩展的——可以添加额外的逻辑(例如,计算总数),而不会改变构造函数的清晰设计。

来源

PHP 构造函数属性提升 - 文档

在本文中,我们探讨了 PHP 的构造函数属性提升,该特性简化了类属性的声明和初始化。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 PHP 教程