【PHP 构造函数与析构函数:从基础到高级的完整指南】
PHP 构造函数与析构函数:从基础到高级的完整指南
一、构造函数:对象的初始化仪式
✅ 1. 基础语法与作用
构造函数在创建对象时自动执行,用于初始化对象属性。
class Person {public $name;public $age;// 构造函数public function __construct($name, $age) {$this->name = $name;$this->age = $age;echo "创建了 {$name},年龄 {$age}\n";}
}// 创建对象时自动调用构造函数
$p1 = new Person("张三", 25); // 输出:创建了 张三,年龄 25
✅ 2. PHP 8.0+ 构造器属性提升(Constructor Property Promotion)
传统写法(繁琐)
class Point {public $x;public $y;public $z;public function __construct($x, $y, $z) {$this->x = $x;$this->y = $y;$this->z = $z;}
}
新写法(简洁)
class Point {public function __construct(public $x,public $y,public $z) {// 属性自动创建并赋值,构造函数体可为空}
}
支持的修饰符:
public
protected
private
readonly
(PHP 8.1+)
class Product {public function __construct(public readonly int $id,public string $name,protected float $price) {// id 是只读的,创建后不能修改}
}
✅ 3. 参数与默认值
class Rectangle {public function __construct(public float $width = 1.0,public float $height = 1.0) {// 支持默认值}
}// 使用默认值
$rect1 = new Rectangle(); // width=1.0, height=1.0
$rect2 = new Rectangle(5.0); // width=5.0, height=1.0
$rect3 = new Rectangle(3.0, 4.0); // width=3.0, height=4.0
✅ 4. 继承中的构造函数
重要规则:子类必须手动调用父类构造函数
class Animal {public function __construct(protected string $name) {echo "动物 {$this->name} 已创建\n";}
}class Dog extends Animal {public function __construct(string $name,private string $breed) {// ⚠️ 必须手动调用父类构造函数parent::__construct($name);echo "品种: {$this->breed}\n";}
}$dog = new Dog('旺财', '金毛');
// 输出:
// 动物 旺财 已创建
// 品种: 金毛
父类无构造函数时
class Animal {// 没有构造函数
}class Dog extends Animal {public function __construct(string $name) {// 不需要调用 parent::__construct()echo "狗 {$name} 已创建\n";}
}
✅ 5. 静态工厂方法(Static Factory Methods)
用于替代复杂构造逻辑,提高代码可读性。
class Product {private function __construct(private ?int $id,private ?string $name) {}// 静态工厂方法public static function fromBasicData(int $id, string $name): static {return new static($id, $name);}public static function fromJson(string $json): static {$data = json_decode($json, true);return new static($data['id'], $data['name']);}public static function createEmpty(): static {return new static(null, null);}
}// 使用示例
$product1 = Product::fromBasicData(1, '手机');
$product2 = Product::fromJson('{"id":2,"name":"电脑"}');
$product3 = Product::createEmpty();
二、析构函数:对象的清理仪式
✅ 1. 基础语法与作用
析构函数在对象销毁时自动执行,用于清理资源。
class FileHandler {private $file;public function __construct(string $filename) {$this->file = fopen($filename, 'r');echo "文件 {$filename} 已打开\n";}public function __destruct() {if ($this->file) {fclose($this->file);echo "文件已关闭\n";}}
}// 使用示例
$handler = new FileHandler('data.txt');
// 当 $handler 超出作用域或被 unset 时
// 自动执行 __destruct()
✅ 2. 执行时机
场景1:脚本结束
$obj = new MyClass();
// 脚本结束时自动调用 __destruct()
场景2:unset()
函数
$obj = new MyClass();
unset($obj); // 立即调用 __destruct()
场景3:变量超出作用域
function test() {$obj = new MyClass(); // 创建// 函数结束时 $obj 被销毁,调用 __destruct()
}
test();
场景4:exit()
时
$obj = new MyClass();
exit(); // 仍会执行 __destruct()
✅ 3. 多对象销毁顺序
class Test {public function __construct(private string $name) {}public function __destruct() {echo "销毁 {$this->name}\n";}
}$a = new Test('A');
$b = new Test('B');
$c = new Test('C');// 输出顺序(后创建的先销毁):
// 销毁 C
// 销毁 B
// 销毁 A
❌ 4. 重要限制与注意事项
限制1:不能抛出异常
public function __destruct() {// ❌ 致命错误!// throw new Exception("清理失败");// ✅ 正确做法:记录日志error_log("清理失败: " . $e->getMessage());
}
限制2:不能有参数
// ❌ 错误
// public function __destruct($param) {}
限制3:不能是静态方法
// ❌ 错误
// public static function __destruct() {}
注意:可能不执行的情况
- 脚本被
die()
或exit()
强制终止 - 发生死循环
- 服务器崩溃
三、特殊场景与最佳实践
✅ 1. 单例模式中的构造函数
class Singleton {private static ?Singleton $instance = null;// 私有构造函数,防止外部创建private function __construct() {}public static function getInstance(): Singleton {if (self::$instance === null) {self::$instance = new self();}return self::$instance;}// 私有克隆函数private function __clone() {}
}
✅ 2. 克隆对象时的构造
class Person {public function __construct(public string $name) {echo "构造: {$this->name}\n";}public function __clone() {// 克隆时不会调用构造函数echo "克隆: {$this->name}\n";}
}$p1 = new Person("张三"); // 输出:构造: 张三
$p2 = clone $p1; // 输出:克隆: 张三
✅ 3. 与序列化的配合
class User {public function __construct(public string $name) {}public function __sleep() {// 序列化前执行return ['name'];}public function __wakeup() {// 反序列化后执行(类似构造函数)echo "反序列化: {$this->name}\n";}
}$user = new User("李四");
$serialized = serialize($user);
$unserialized = unserialize($serialized); // 输出:反序列化: 李四
四、常见错误与调试
❌ 错误1:忘记调用父类构造函数
class Dog extends Animal {public function __construct(string $name, string $breed) {// ❌ 忘记调用 parent::__construct($name)$this->breed = $breed;}
}
❌ 错误2:析构函数抛出异常
public function __destruct() {// ❌ 这样会中断脚本// throw new Exception("Error");
}
✅ 调试技巧
class DebugClass {public function __construct() {error_log("对象创建");}public function __destruct() {error_log("对象销毁");}
}
五、终极总结
特性 | 说明 |
---|---|
构造函数 | __construct() ,PHP 8.0+ 支持属性提升 |
参数默认值 | 支持字面量和常量 |
继承规则 | 子类必须手动调用 parent::__construct() |
析构函数 | __destruct() 用于资源清理 |
执行时机 | 对象销毁时(脚本结束、unset、超出作用域) |
重要限制 | 析构函数不能抛异常、不能有参数 |
✅ 最佳实践:
- 使用构造器属性提升减少样板代码
- 析构函数只用于资源清理(文件、数据库连接等)
- 复杂对象创建使用静态工厂方法
- 子类构造函数必须调用父类构造函数
- 析构函数不要抛出异常,用日志记录错误
🚀 记住:构造函数是对象的"出生证明",析构函数是对象的"告别仪式",合理使用它们能让代码更健壮!