ZetCode

PHP 属性教程

上次修改时间:2025 年 5 月 18 日

在本文中,我们介绍了 PHP 属性。

PHP 中的属性是属于类的变量,封装数据并允许对象存储相关信息。每个属性都与特定的可见性级别相关联,决定了它在程序中的可访问性。属性也可以是静态的或非静态的,影响它们是属于类还是实例。

PHP 属性的类型

属性对于 PHP 中的面向对象编程至关重要,允许类维护状态和行为。它们可以用各种可见性级别声明,并且 PHP 7.4 引入了类型属性以提高类型安全性。PHP 8.0 增加了构造函数属性提升,减少了在构造函数中初始化属性时的样板代码。

基本属性声明

属性在类中使用以下可见性关键字之一声明:public、protected 或 private。公共属性可以从任何地方访问,受保护的属性只能在类及其后代中访问,而私有属性只能在声明类中访问。

basic_properties.php
<?php

declare(strict_types=1);

class User {
    public $name;
    protected $email;
    private $password;

    public function __construct($name, $email, $password) {
        $this->name = $name;
        $this->email = $email;
        $this->password = $password;
    }

    public function getEmail() {
        return $this->email;
    }
}

$user = new User('John Doe', 'john@example.com', 'secret123');

echo "Name: {$user->name}\n";        // Works - public
// echo "Email: {$user->email}\n";    // Error - protected
// echo "Pass: {$user->password}\n";  // Error - private
echo "Email: {$user->getEmail()}\n"; // Works - accessed via method

此示例展示了具有不同可见性级别的属性声明。公共属性可以直接访问,而受保护和私有属性需要方法才能访问它们。构造函数在创建新对象时初始化所有属性。

λ php basic_properties.php
Name: John Doe
Email: john@example.com

属性类型声明

PHP 7.4 引入了类型属性,允许您指定属性的类型。这有助于尽早捕获与类型相关的错误,并使代码更易于维护。支持的类型包括所有 PHP 类型和自定义类。

typed_properties.php
<?php

declare(strict_types=1);

class Product {

    public int $id;
    public string $name;
    public float $price;
    public bool $inStock;
    public ?DateTime $createdAt; // Nullable

    public function __construct(int $id, string $name, float $price) {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
        $this->inStock = true;
        $this->createdAt = new DateTime();
    }

    public function setPrice(float $newPrice): void {
        if ($newPrice <= 0) {
            throw new InvalidArgumentException("Price must be positive");
        }
        $this->price = $newPrice;
    }
}

$product = new Product(1, 'Laptop', 999.99);
$product->setPrice(899.99);

echo "Product: {$product->name}, Price: {$product->price}\n";
echo "Created at: {$product->createdAt->format('Y-m-d')}\n";

// This would cause a TypeError
// $product->price = "free";

类型属性确保值与声明的类型匹配。该示例显示了各种类型声明,包括可空类型(带有 ? 前缀)。类型检查发生在赋值和访问属性时。

λ php typed_properties.php
Product: Laptop, Price: 899.99
Created at: 2025-05-18

静态属性

静态属性属于类,而不是任何对象实例。它们在类的所有实例之间共享,无需创建对象即可访问。使用 static 关键字来声明它们。

static_properties.php
<?php

declare(strict_types=1);

class Counter {

    public static int $count = 0;
    public int $instanceCount = 0;

    public function __construct() {
        self::$count++;
        $this->instanceCount++;
    }

    public static function getCount(): int {
        return self::$count;
    }
}

// Access static property without creating an instance
echo "Initial count: " . Counter::$count . "\n";

$c1 = new Counter();
$c2 = new Counter();
$c3 = new Counter();

echo "Static count: " . Counter::getCount() . "\n";
echo "Instance 1 count: {$c1->instanceCount}\n";
echo "Instance 2 count: {$c2->instanceCount}\n";
echo "Instance 3 count: {$c3->instanceCount}\n";

// Late Static Binding
class ParentClass {
    protected static string $name = 'Parent';

    public static function getName(): string {
        return static::$name; // 'static' instead of 'self'
    }
}

class ChildClass extends ParentClass {
    protected static string $name = 'Child';
}

echo ParentClass::getName() . "\n"; // Outputs 'Parent'
echo ChildClass::getName() . "\n";  // Outputs 'Child'

静态属性在所有实例中保持其值。该示例演示了静态 $count 如何为每个新对象递增,而 $instanceCount 保持每个实例特有。后期静态绑定允许子类覆盖静态属性。

self::$count++;

self:: 关键字用于从类本身内部访问类的静态成员。它允许您引用静态属性和方法,而无需创建类的实例。

$this->instanceCount++;

$this 关键字用于引用类的当前实例。它允许您从类内部访问实例属性和方法。在这种情况下,它会为创建的每个新对象递增实例特定的属性 $instanceCount

λ php static_properties.php
Initial count: 0
Static count: 3
Instance 1 count: 1
Instance 2 count: 1
Instance 3 count: 1
Parent
Child

构造函数中的属性提升

PHP 8.0 引入了构造函数属性提升,允许直接在构造函数参数中声明属性。这减少了当属性仅用于存储构造函数参数时的样板代码。

property_promotion.php
<?php

declare(strict_types=1);

class Customer {

    public function __construct(
        public string $name,
        protected string $email,
        private string $phone,
        public readonly DateTimeImmutable $createdAt = new DateTimeImmutable()
    ) {
        // No need for $this->name = $name; etc.
    }

    public function getContactInfo(): string {
        return "Email: {$this->email}, Phone: {$this->phone}";
    }
}

$customer = new Customer('Alice Smith', 'alice@example.com', '555-1234');

echo "Customer: {$customer->name}\n";
echo "Created: {$customer->createdAt->format('Y-m-d')}\n";
echo $customer->getContactInfo() . "\n";

// readonly properties can't be modified
// $customer->createdAt = new DateTimeImmutable(); // Error

属性提升将参数声明与属性声明相结合。该示例显示了具有不同可见性的提升属性和具有默认值的只读属性。只读属性只能设置一次,之后不能修改。

λ php property_promotion.php
Customer: Alice Smith
Created: 2025-05-18
Email: alice@example.com, Phone: 555-1234

使用 __get 和 __set 的魔术属性

PHP 允许通过魔术方法 __get__set 动态访问属性。当访问不存在或不可访问的属性时,会调用这些方法,从而实现灵活的属性处理。

magic_properties.php
<?php

declare(strict_types=1);

class DynamicConfig {
    private array $data = [];
    private array $allowed = ['timeout', 'debug', 'log_level'];

    public function __get(string $name): mixed {
        if (in_array($name, $this->allowed)) {
            return $this->data[$name] ?? null;
        }
        throw new OutOfBoundsException("Property $name doesn't exist");
    }

    public function __set(string $name, mixed $value): void {
        if (in_array($name, $this->allowed)) {
            $this->data[$name] = $value;
        } else {
            throw new OutOfBoundsException("Cannot set $name");
        }
    }

    public function __isset(string $name): bool {
        return isset($this->data[$name]);
    }

    public function __unset(string $name): void {
        unset($this->data[$name]);
    }
}

$config = new DynamicConfig();
$config->timeout = 30;
$config->debug = true;

echo "Timeout: {$config->timeout}\n";
echo "Debug: " . ($config->debug ? 'ON' : 'OFF') . "\n";

var_dump(isset($config->timeout)); // true
var_dump(isset($config->log_level)); // false

// This would throw an exception
// $config->invalid = 'value';

魔术方法提供了对属性访问的控制。该示例实现了一个允许的属性白名单。__get 检索值,__set 存储它们,__isset 检查存在性,__unset 删除属性,所有这些都带有验证。

λ php magic_properties.php
Timeout: 30
Debug: ON
bool(true)
bool(false)

只读属性

PHP 8.1 引入了只读属性,这些属性只能初始化一次,然后保持不变。它们对于创建值对象和数据传输对象(DTO)很有用,在这些对象中,属性在构造后不应更改。

readonly_properties.php
<?php

declare(strict_types=1);

class Transaction {

    public readonly string $id;
    public readonly float $amount;
    public readonly DateTimeImmutable $createdAt;

    public function __construct(float $amount) {
        $this->id = uniqid('txn_');
        $this->amount = $amount;
        $this->createdAt = new DateTimeImmutable();
    }

    // Can't have a setter that modifies readonly properties
    // public function setAmount(float $amount): void {
    //     $this->amount = $amount; // Error
    // }
}

// PHP 8.2 allows readonly classes
readonly class Point {
    public function __construct(
        public float $x,
        public float $y
    ) {}
}

$txn = new Transaction(99.99);
echo "Transaction: {$txn->id}, Amount: {$txn->amount}\n";
echo "Created: {$txn->createdAt->format('Y-m-d H:i:s')}\n";

$point = new Point(3.5, 4.2);
echo "Point: ({$point->x}, {$point->y})\n";

// These would cause errors:
// $txn->amount = 100;
// $point->x = 5;

只读属性提供不变性保证。该示例显示了单独的只读属性和一个只读类(PHP 8.2+)。一旦设置,这些属性就无法修改,从而使对象更可预测且线程安全。

λ php readonly_properties.php
Transaction: txn_6647f5a3e3a63, Amount: 99.99
Created: 2025-05-18 00:00:00
Point: (3.5, 4.2)

属性初始化和默认值

属性可以在声明时使用默认值进行初始化。PHP 7.4+ 允许类型属性具有与其类型匹配的默认值。

property_initialization.php
<?php

declare(strict_types=1);

class Settings {

    // Basic initialization
    public string $theme = 'light';
    public int $itemsPerPage = 10;

    // Typed properties with defaults
    public bool $notificationsEnabled = true;
    public ?string $apiKey = null;

    // Complex defaults with constants
    public const DEFAULT_LOCALE = 'en_US';
    public string $locale = self::DEFAULT_LOCALE;

    // Uninitialized typed properties
    public DateTime $lastUpdated; // Must be initialized before access

    // Static property initialization
    public static int $cacheLifetime = 3600;

    public function __construct() {
        $this->lastUpdated = new DateTime();
    }

    public function updateLastModified(): void {
        $this->lastUpdated = new DateTime();
    }
}

$settings = new Settings();
echo "Theme: {$settings->theme}\n";
echo "Items per page: {$settings->itemsPerPage}\n";
echo "Last updated: {$settings->lastUpdated->format('Y-m-d')}\n";

// Modify properties
$settings->theme = 'dark';
$settings->itemsPerPage = 25;
$settings->updateLastModified();

echo "Modified theme: {$settings->theme}\n";
echo "Modified last updated: {$settings->lastUpdated->format('H:i:s')}\n";

此示例演示了各种属性初始化技术。没有默认值的类型属性必须在访问之前进行初始化。常量可以提供默认值。静态属性在加载类时初始化一次。

λ php property_initialization.php
Theme: light
Items per page: 10
Last updated: 2025-05-21
Modified theme: dark
Modified last updated: 08:42:32

PHP 属性是 PHP 中面向对象编程的基础。关于属性要记住的要点

在本文中,我们介绍了 PHP 属性的基础知识,包括它们的声明、可见性、静态属性、类型属性和构造函数属性提升。我们还讨论了魔术属性、只读属性和属性初始化技术。

作者

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

列出 所有 PHP 教程