PHP项目构造器与析构顺序

wen PHP项目 1

本文目录导读:

PHP项目构造器与析构顺序

  1. 单个类的基础顺序
  2. 继承关系中的顺序(重点)
  3. 不调用 parent:: 的情况
  4. 多个对象 + 引用计数的析构顺序
  5. 静态属性与全局变量的析构顺序
  6. 核心原则总结

在 PHP 中,构造函数(__construct())和析构函数(__destruct())的执行顺序有明确的规则,尤其是在继承对象生命周期的上下文中。

单个类的基础顺序

对于单一类,顺序非常直接:

class MyClass {
    public function __construct() {
        echo "构造执行\n";
    }
    public function __destruct() {
        echo "析构执行\n";
    }
}
$obj = new MyClass();
echo "对象使用中\n";
unset($obj); // 或脚本结束
// 输出:
// 构造执行
// 对象使用中
// 析构执行

规则:构造 → 使用 → 析构


继承关系中的顺序(重点)

当存在继承时,PHP 不会自动调用父类的构造/析构方法,需要显式调用(通常通过 parent::)。

1 标准执行顺序

class ParentClass {
    public function __construct() {
        echo "父类构造\n";
    }
    public function __destruct() {
        echo "父类析构\n";
    }
}
class ChildClass extends ParentClass {
    public function __construct() {
        parent::__construct(); // 显式调用
        echo "子类构造\n";
    }
    public function __destruct() {
        echo "子类析构\n";
        parent::__destruct(); // 显式调用
    }
}
$obj = new ChildClass();
unset($obj);
// 输出:
// 父类构造
// 子类构造
// 子类析构
// 父类析构

构造顺序:父类 → 子类(类似叠箱子,从外到内构建基础)
析构顺序:子类 → 父类(与构造相反,后构建的先销毁)


不调用 parent:: 的情况

class ChildWithoutParentCall extends ParentClass {
    public function __construct() {
        echo "子类构造\n";
        // 没有调用 parent::__construct()
    }
}
$obj = new ChildWithoutParentCall();
// 输出:
// 子类构造        ← 父类构造不会自动执行

PHP 不会自动调用父类的构造/析构方法,必须显式调用。


多个对象 + 引用计数的析构顺序

当多个对象相互引用时,析构顺序取决于引用计数归零的时间:

class ObjA {
    public function __destruct() {
        echo "A 析构\n";
    }
}
class ObjB {
    public $a;
    public function __destruct() {
        echo "B 析构\n";
    }
}
$a = new ObjA();
$b = new ObjB();
$b->a = $a; // A 的引用计数增加
unset($a);   // 这里不会触发 A 的析构,因为 B 还引用它
unset($b);   // 先析构 B,A 的引用计数归零,再析构 A
// 输出:
// B 析构
// A 析构

引用计数归零的先后顺序决定析构顺序。


静态属性与全局变量的析构顺序

class StaticExample {
    public function __destruct() {
        echo "Static 析构\n";
    }
}
$globalObj = new StaticExample();
function test() {
    $localObj = new StaticExample();
}
test(); // 局部变量在函数结束后析构
echo "脚本结束前\n";
// 输出:
// Static 析构    ← 局部变量
// 脚本结束前
// Static 析构    ← 全局变量(在脚本结束时析构)

规则

  • 局部变量:函数/方法结束时析构
  • 全局变量:脚本结束时析构(顺序按变量定义顺序,但受引用影响)
  • 静态变量:在全局变量之后析构

核心原则总结

场景 构造顺序 析构顺序
继承(显式调用 parent:: 父类 → 子类 子类 → 父类(后进先出)
继承(不调用 parent:: 只执行子类构造 只执行子类析构
多个独立对象 按 new 顺序 按 unset/引用计数归零顺序
全局 vs 局部对象 局部优先于全局

最佳实践

  1. 在子类构造中始终先调用 parent::__construct()
  2. 在子类析构中始终先执行子类逻辑,再调用 parent::__destruct()
  3. 避免在析构函数中抛出异常(PHP 8.0+ 会将其转为致命错误)

记忆口诀

构造像搭积木,从地基往上垒
析构像拆建筑,先拆顶层再拆底

抱歉,评论功能暂时关闭!