PHP 入门教程
22.2
22.3
22.4
22.5
22.6
22.7
22.8
22.9
PHP 魔术方法
本教程共 65 篇 · 第 22 篇 · 更新于 2026-07-24 · 约 6 分钟阅读
PHPPHP8魔术方法__toString__get__call__construct
22. PHP 魔术方法
本节目标:掌握 PHP 的魔术方法,了解它们在特定场景下的自动调用机制。学完你能让类更智能,与 PHP 内置机制深度整合。
22.1 什么是魔术方法
魔术方法是以双下划线 __ 开头的方法。它们不需要显式调用,PHP 会在特定时机自动触发。
22.2 __toString()
对象被当作字符串使用时自动调用:
<?php
class User {
public function __construct(
public string $name,
public int $age
) {}
public function __toString(): string {
return "User: {$this->name}, {$this->age}岁";
}
}
$user = new User("张三", 25);
echo $user; // User: 张三, 25岁
?>
NotePHP 7.0 起,
__toString()可以声明返回类型string。如果方法内抛出异常,PHP 会将其包装为 Error 抛出。
22.3 __get() 和 __set()
访问或设置不存在(或不可访问)的属性时自动调用:
<?php
class Product {
private array $data = [];
public function __set(string $name, mixed $value): void {
$this->data[$name] = $value;
}
public function __get(string $name): mixed {
return $this->data[$name] ?? null;
}
}
$product = new Product();
$product->name = "手机"; // 调用 __set
$product->price = 2999; // 调用 __set
echo $product->name; // 调用 __get
?>
22.4 __isset() 和 __unset()
对不存在(或不可访问)的属性使用 isset() 或 unset() 时自动调用:
<?php
class Config {
private array $settings = [
'debug' => true,
'timezone' => 'Asia/Shanghai'
];
public function __isset(string $name): bool {
return isset($this->settings[$name]);
}
public function __unset(string $name): void {
unset($this->settings[$name]);
}
}
$config = new Config();
var_dump(isset($config->debug)); // true
unset($config->debug);
var_dump(isset($config->debug)); // false
?>
22.5 __call() 和 __callStatic()
调用不存在(或不可访问)的方法时自动调用:
<?php
class MagicMethods {
public function __call(string $name, array $arguments): mixed {
return "调用了实例方法: $name,参数: " . implode(', ', $arguments);
}
public static function __callStatic(string $name, array $arguments): mixed {
return "调用了静态方法: $name";
}
}
$obj = new MagicMethods();
echo $obj->nonExistentMethod("a", "b");
// 调用了实例方法: nonExistentMethod,参数: a, b
echo MagicMethods::anotherMethod();
// 调用了静态方法: anotherMethod
?>
Tip
__call()常用于实现方法委托或流畅接口(Fluent Interface)。
22.6 __clone()
使用 clone 关键字复制对象时自动调用:
<?php
class Document {
public string $title;
public DateTime $createdAt;
public function __construct(string $title) {
$this->title = $title;
$this->createdAt = new DateTime();
}
public function __clone(): void {
// 深拷贝:创建新的 DateTime 对象
$this->createdAt = clone $this->createdAt;
$this->title .= " (副本)";
}
}
$doc1 = new Document("报告");
$doc2 = clone $doc1;
echo $doc2->title; // 报告 (副本)
?>
NotePHP 默认是浅拷贝。如果对象属性包含对象引用,
__clone()中需要手动深拷贝。
22.7 __sleep() 和 __wakeup()
对象被序列化或反序列化时自动调用:
<?php
class Connection {
public string $host;
private $socket; // 不可序列化的资源
public function __construct(string $host) {
$this->host = $host;
$this->socket = fopen("php://memory", "r");
}
// 序列化前调用:返回需要序列化的属性名
public function __sleep(): array {
return ['host']; // 只序列化 host,不序列化 socket
}
// 反序列化后调用:重建资源
public function __wakeup(): void {
$this->socket = fopen("php://memory", "r");
}
}
$conn = new Connection("localhost");
$serialized = serialize($conn);
$restored = unserialize($serialized);
echo $restored->host; // localhost
?>
TipPHP 7.4+ 推荐使用
__serialize()和__unserialize()替代__sleep()/__wakeup(),它们更灵活,能直接返回序列化数据数组。
22.8 __invoke()
把对象当作函数调用时自动触发:
<?php
class Multiplier {
public function __construct(private int $factor) {}
public function __invoke(int $number): int {
return $number * $this->factor;
}
}
$double = new Multiplier(2);
echo $double(5); // 10
$triple = new Multiplier(3);
echo $triple(5); // 15
?>
Note
__invoke()让对象可调用,常用于创建可调用的策略对象或闭包替代方案。
22.9 __debugInfo()
使用 var_dump() 输出对象时自动调用:
<?php
class BankAccount {
public string $owner;
private float $balance;
private string $password;
public function __construct(string $owner, float $balance) {
$this->owner = $owner;
$this->balance = $balance;
$this->password = "secret123";
}
public function __debugInfo(): array {
// 隐藏敏感信息
return [
'owner' => $this->owner,
'balance' => $this->balance,
'password' => '***隐藏***'
];
}
}
$account = new BankAccount("张三", 10000);
var_dump($account);
// 不会暴露真实密码
?>
22.10 魔术方法速查表
| 魔术方法 | 触发时机 | 常用场景 |
|---|---|---|
__construct() | new 创建对象 | 初始化属性 |
__destruct() | 对象销毁 | 资源清理 |
__toString() | 对象转字符串 | 输出格式化 |
__get($name) | 读取不可访问属性 | 动态属性 |
__set($name, $val) | 设置不可访问属性 | 动态属性 |
__isset($name) | 对不可访问属性用 isset | 动态属性 |
__unset($name) | 对不可访问属性用 unset | 动态属性 |
__call($name, $args) | 调用不可访问方法 | 方法委托 |
__callStatic($name, $args) | 静态调用不可访问方法 | 静态委托 |
__clone() | clone 对象 | 深拷贝 |
__sleep() | serialize() | 选择序列化字段 |
__wakeup() | unserialize() | 重建资源 |
__invoke() | 对象当作函数调用 | 可调用对象 |
__debugInfo() | var_dump() | 控制调试输出 |
22.11 使用建议
<?php
// 1. 不要过度使用魔术方法,会降低代码可读性
// 2. 优先使用显式方法,魔术方法作为补充
// 3. 注意性能:魔术方法比直接访问稍慢
// 4. IDE 提示:魔术方法中的动态属性/方法无法被 IDE 自动补全
// 好的实践:在框架中封装魔术方法,业务代码保持显式
class ActiveRecord {
protected array $attributes = [];
public function __get(string $name): mixed {
return $this->attributes[$name] ?? null;
}
// 同时提供显式方法
public function getAttribute(string $name): mixed {
return $this->attributes[$name] ?? null;
}
}
?>
来源:参考了 w3cschool「PHP 面向对象」、runoob「PHP 面向对象」以及 php.net 官方文档,综合改写后所得。