【PHP 构造函数与析构函数:从基础到高级的完整指南】

发布于:2025-08-02 ⋅ 阅读:(9) ⋅ 点赞:(0)

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、超出作用域)
重要限制 析构函数不能抛异常、不能有参数

最佳实践

  1. 使用构造器属性提升减少样板代码
  2. 析构函数只用于资源清理(文件、数据库连接等)
  3. 复杂对象创建使用静态工厂方法
  4. 子类构造函数必须调用父类构造函数
  5. 析构函数不要抛出异常,用日志记录错误

🚀 记住:构造函数是对象的"出生证明",析构函数是对象的"告别仪式",合理使用它们能让代码更健壮!


网站公告

今日签到

点亮在社区的每一天
去签到