PHP 构造函数与析构函数
概述
构造函数(Constructor)在创建对象时自动调用,用于初始化对象状态。析构函数(Destructor)在对象的所有引用被删除或对象被显式销毁时调用,用于清理资源。
版本要求
- PHP 8.0+:构造器属性提升、
new表达式中的对象 - PHP 8.1+:构造函数参数中的
new初始化
基础概念
构造函数 __construct
构造函数是 PHP 魔术方法之一,在 new 创建实例时自动调用。适合执行初始化操作。
php
<?php
declare(strict_types=1);
class DatabaseConnection
{
private \PDO $pdo;
public function __construct(
private readonly string $dsn,
private readonly string $username = '',
private readonly string $password = '',
) {
$this->pdo = new \PDO($dsn, $username, $password);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
public function getPdo(): \PDO
{
return $this->pdo;
}
}
$db = new DatabaseConnection('mysql:host=localhost;dbname=test', 'root', 'secret');析构函数 __destruct
析构函数在对象引用归零或显式销毁时调用。
php
<?php
declare(strict_types=1);
class FileHandler
{
private $handle;
public function __construct(string $path)
{
$this->handle = fopen($path, 'r');
echo "File opened: {$path}\n";
}
public function __destruct()
{
if (is_resource($this->handle)) {
fclose($this->handle);
echo "File closed\n";
}
}
}
$handler = new FileHandler('/tmp/test.txt');
// 脚本结束时或 $handler = null 时调用 __destruct语法与代码
构造器属性提升(PHP 8.0+)
构造函数参数带修饰符时,PHP 同时将其作为类属性,并自动赋值。
php
<?php
declare(strict_types=1);
// PHP 8.0+ 属性提升写法
class Point
{
public function __construct(
public int $x,
public int $y = 0,
) {}
}
$p = new Point(4, 5);
echo $p->x; // 4
echo $p->y; // 5继承中的构造函数
子类定义了构造函数后不会隐式调用父类构造函数,需要手动调用 parent::__construct()。
php
<?php
declare(strict_types=1);
class BaseClass
{
public function __construct(
protected string $name = 'base',
) {
echo "BaseClass constructor: {$this->name}\n";
}
}
class SubClass extends BaseClass
{
public function __construct(
string $name,
private int $value = 0,
) {
parent::__construct($name);
echo "SubClass constructor: {$this->name}, value={$this->value}\n";
}
}
class OtherSubClass extends BaseClass
{
// 不定义构造函数,自动继承 BaseClass 的构造函数
}
new BaseClass(); // BaseClass constructor: base
new SubClass('child', 42); // BaseClass constructor: child
// SubClass constructor: child, value=42
new OtherSubClass(); // BaseClass constructor: base命名参数(PHP 8.0+)
构造函数支持命名参数调用,参数顺序无关。
php
<?php
declare(strict_types=1);
class QueryBuilder
{
public function __construct(
private string $table,
private array $where = [],
private int $limit = 100,
private int $offset = 0,
) {}
}
// 命名参数(可跳过中间参数)
$q = new QueryBuilder(table: 'users', limit: 10, offset: 20);初始化中 new 对象(PHP 8.1+)
PHP 8.1 起允许在默认参数值中使用 new。
php
<?php
declare(strict_types=1);
class Service
{
public function __construct(
private readonly string $name,
\DateTimeImmutable $createdAt = new \DateTimeImmutable(),
) {}
}
$service = new Service('MyService');WARNING
禁止动态类名、匿名类、参数解包等不支持的常量表达式。
静态工厂方法
PHP 每个类只能有一个构造函数。当需要多种创建方式时,使用静态工厂方法。
php
<?php
declare(strict_types=1);
class Money
{
private function __construct(
private readonly int $amount,
private readonly string $currency,
) {}
public static function fromAmount(int $amount, string $currency = 'CNY'): self
{
return new self($amount, $currency);
}
public static function fromCents(int $cents, string $currency = 'CNY'): self
{
return new self((int) ($cents / 100), $currency);
}
public static function fromString(string $value): self
{
if (!preg_match('/^(\d+)\s*([A-Z]{3})$/', $value, $matches)) {
throw new \InvalidArgumentException("Invalid money format: {$value}");
}
return new self((int) $matches[1], $matches[2]);
}
public function getAmount(): int
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
}
$m1 = Money::fromAmount(1000);
$m2 = Money::fromCents(15000);
$m3 = Money::fromString('100 CNY');详细说明
析构函数的调用时机
- 脚本结束时所有对象引用被销毁
- 对象变量被
unset()或赋值为null - 当对象引用计数归零
WARNING
析构函数中抛出异常(脚本关闭时)会导致致命错误。析构函数中调用 exit() 会中止其余关闭操作。
循环引用与垃圾回收
两个对象互相引用时,引用计数不会归零,析构函数不会被自动调用。需要 gc_collect_cycles() 手动触发。
php
<?php
declare(strict_types=1);
class Node
{
public ?Node $next = null;
public function __construct(public readonly string $name) {}
public function __destruct()
{
echo "Destroying: {$this->name}\n";
}
}
$a = new Node('A');
$b = new Node('B');
$a->next = $b;
$b->next = $a;
$a = null;
$b = null;
// 此时析构函数不会被调用(循环引用)
gc_collect_cycles();
// Destroying: A
// Destroying: B实战示例
依赖注入模式
php
<?php
declare(strict_types=1);
interface LoggerInterface
{
public function log(string $message): void;
}
class FileLogger implements LoggerInterface
{
public function log(string $message): void
{
echo "[LOG] {$message}\n";
}
}
class UserService
{
public function __construct(
private readonly LoggerInterface $logger,
private readonly \PDO $db,
) {}
public function createUser(string $name, string $email): void
{
$this->logger->log("Creating user: {$name}");
$stmt = $this->db->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]);
$this->logger->log("User created: {$name}");
}
}
$logger = new FileLogger();
$pdo = new \PDO('sqlite::memory:');
$service = new UserService($logger, $pdo);
$service->createUser('Alice', 'alice@example.com');注意事项
- 构造函数不受签名兼容性规则约束:子类构造函数签名可以完全不同
- 析构函数中避免抛出异常:脚本关闭时会导致致命错误
- 循环引用注意内存泄漏:使用弱引用(WeakReference)或手动 gc_collect_cycles()
- callable 不能用于属性提升:会导致引擎混淆
最佳实践
- 构造函数做最少的工作:只做必要的初始化,复杂逻辑放到专门方法
- 使用依赖注入:通过构造函数传入依赖,而非在内部创建
- 属性提升减少样板代码:PHP 8.0+ 优先使用构造器属性提升
- 私有构造函数+工厂方法:需要控制实例化方式时使用
- 不可变对象使用 readonly:构造函数中设置后不再变化
进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');