构造函数和析构函数

构造函数和析构函数是PHP面向对象编程中两个非常重要的特殊方法。它们分别在对象创建和销毁时自动调用,为我们提供了初始化资源和清理资源的机会。

学习目标

完成本节学习后,你将能够:

  • 理解构造函数的作用和使用场景
  • 掌握构造函数的定义和使用方法
  • 学会通过构造函数实现依赖注入
  • 理解析构函数的作用和执行时机
  • 掌握资源管理的基本技巧
  • 了解对象生命周期的完整过程

构造函数(Constructor)

什么是构造函数

构造函数是在创建对象时自动调用的特殊方法。它的主要作用是:

  1. 初始化对象属性:为对象的属性设置初始值
  2. 分配资源:如数据库连接、文件句柄等
  3. 执行验证:确保传入的参数符合要求
  4. 建立对象状态:确保对象处于可用状态

构造函数的语法

<?php
class MyClass {
    // PHP 5.3.3+ 可以使用类名作为构造函数(不推荐)
    // public function MyClass() {
    //     echo "这是旧式的构造函数";
    // }

    // 现代推荐的构造函数语法
    public function __construct() {
        echo "构造函数被调用\n";
    }
}
?>

基本构造函数示例

<?php
class User {
    private $id;
    private $username;
    private $email;
    private $createdAt;

    // 基本构造函数
    public function __construct() {
        echo "创建了一个新用户对象\n";
        $this->id = null;
        $this->username = 'guest';
        $this->email = '';
        $this->createdAt = date('Y-m-d H:i:s');
    }

    // 获取器方法
    public function getUsername() {
        return $this->username;
    }

    public function getEmail() {
        return $this->email;
    }

    public function getCreatedAt() {
        return $this->createdAt;
    }
}

// 使用基本构造函数
echo "=== 基本构造函数示例 ===\n";
$user1 = new User();  // 输出:创建了一个新用户对象
echo "用户名:{$user1->getUsername()}\n";
echo "创建时间:{$user1->getCreatedAt()}\n";
?>

带参数的构造函数

<?php
class Product {
    private $id;
    private $name;
    private $price;
    private $category;
    private $inStock;

    // 带参数的构造函数
    public function __construct($name, $price, $category = '默认分类') {
        echo "创建产品:$name\n";

        $this->id = uniqid('product_');
        $this->name = $this->validateName($name);
        $this->price = $this->validatePrice($price);
        $this->category = $category;
        $this->inStock = true;
    }

    // 验证产品名称
    private function validateName($name) {
        if (empty($name)) {
            throw new Exception("产品名称不能为空");
        }
        if (strlen($name) > 100) {
            throw new Exception("产品名称过长");
        }
        return trim($name);
    }

    // 验证价格
    private function validatePrice($price) {
        if (!is_numeric($price) || $price < 0) {
            throw new Exception("价格必须是非负数");
        }
        return (float)$price;
    }

    // 获取器方法
    public function getId() {
        return $this->id;
    }

    public function getName() {
        return $this->name;
    }

    public function getPrice() {
        return $this->price;
    }

    public function getCategory() {
        return $this->category;
    }

    public function isInStock() {
        return $this->inStock;
    }

    // 设置库存状态
    public function setInStock($inStock) {
        $this->inStock = (bool)$inStock;
    }

    // 获取产品信息
    public function getInfo() {
        $status = $this->inStock ? "有货" : "缺货";
        return sprintf(
            "产品:%s | 价格:¥%.2f | 分类:%s | 状态:%s",
            $this->name,
            $this->price,
            $this->category,
            $status
        );
    }
}

// 使用带参数的构造函数
echo "=== 带参数的构造函数示例 ===\n";

try {
    $product1 = new Product("笔记本电脑", 5999.99, "电子产品");
    $product2 = new Product("办公椅", 299.50);  // 使用默认分类
    $product3 = new Product("咖啡杯", 15.80);

    echo $product1->getInfo() . "\n";
    echo $product2->getInfo() . "\n";
    echo $product3->getInfo() . "\n";

} catch (Exception $e) {
    echo "错误:" . $e->getMessage() . "\n";
}
?>

构造函数中的复杂初始化

<?php
class DatabaseConnection {
    private $connection;
    private $host;
    private $database;
    private $isConnected = false;

    // 复杂的构造函数初始化
    public function __construct($host, $database, $username, $password) {
        echo "正在初始化数据库连接...\n";

        $this->host = $this->validateHost($host);
        $this->database = $this->validateDatabase($database);

        try {
            $this->connection = $this->createConnection($host, $database, $username, $password);
            $this->isConnected = true;
            echo "数据库连接成功!\n";

            // 执行初始化设置
            $this->initializeDatabase();

        } catch (Exception $e) {
            echo "数据库连接失败:" . $e->getMessage() . "\n";
            $this->isConnected = false;
            throw $e;  // 重新抛出异常
        }
    }

    private function validateHost($host) {
        if (empty($host)) {
            throw new Exception("数据库主机不能为空");
        }
        return $host;
    }

    private function validateDatabase($database) {
        if (empty($database)) {
            throw new Exception("数据库名称不能为空");
        }
        return $database;
    }

    private function createConnection($host, $database, $username, $password) {
        // 模拟数据库连接创建
        echo "创建连接到 $host/$database\n";

        // 在实际应用中,这里会使用 PDO 或 mysqli
        // 这里我们模拟连接过程
        if ($username === 'wrong' || $password === 'wrong') {
            throw new Exception("认证失败");
        }

        return "模拟数据库连接对象";
    }

    private function initializeDatabase() {
        echo "执行数据库初始化设置...\n";
        // 设置字符编码
        // 设置时区
        // 创建必要的表等
        echo "数据库初始化完成\n";
    }

    public function isConnected() {
        return $this->isConnected;
    }

    public function getConnection() {
        return $this->connection;
    }

    public function getHost() {
        return $this->host;
    }

    public function getDatabase() {
        return $this->database;
    }
}

// 使用复杂初始化的构造函数
echo "=== 复杂初始化构造函数示例 ===\n";

try {
    // 正常连接
    $db1 = new DatabaseConnection("localhost", "myapp", "user", "pass");
    echo "数据库状态:" . ($db1->isConnected() ? "已连接" : "未连接") . "\n";
    echo "连接信息:{$db1->getHost()}/{$db1->getDatabase()}\n\n";

    // 错误连接
    echo "尝试错误的连接...\n";
    $db2 = new DatabaseConnection("localhost", "myapp", "wrong", "pass");

} catch (Exception $e) {
    echo "捕获到异常:" . $e->getMessage() . "\n";
}
?>

依赖注入(Dependency Injection)

依赖注入是构造函数的重要用途之一,它让代码更加灵活和可测试。

<?php
class Logger {
    private $logFile;

    public function __construct($logFile = 'app.log') {
        $this->logFile = $logFile;
        echo "Logger 初始化,日志文件:$logFile\n";
    }

    public function log($message) {
        $timestamp = date('Y-m-d H:i:s');
        $logMessage = "[$timestamp] $message\n";
        echo "记录日志:$logMessage";
        // 在实际应用中会写入文件
        // file_put_contents($this->logFile, $logMessage, FILE_APPEND);
    }
}

class EmailService {
    private $smtpServer;
    private $logger;

    // 依赖注入构造函数
    public function __construct($smtpServer, Logger $logger) {
        $this->smtpServer = $smtpServer;
        $this->logger = $logger;
        echo "EmailService 初始化,SMTP服务器:$smtpServer\n";

        $this->logger->log("EmailService 已启动");
    }

    public function sendEmail($to, $subject, $message) {
        $this->logger->log("准备发送邮件到: $to");

        // 模拟邮件发送
        echo "发送邮件到:$to\n";
        echo "主题:$subject\n";
        echo "内容:$message\n";

        $this->logger->log("邮件发送成功到: $to");
        return true;
    }

    public function getSmtpServer() {
        return $this->smtpServer;
    }
}

class UserService {
    private $emailService;
    private $logger;

    // 多依赖注入
    public function __construct(EmailService $emailService, Logger $logger) {
        $this->emailService = $emailService;
        $this->logger = $logger;
        echo "UserService 初始化完成\n";

        $this->logger->log("UserService 已启动");
    }

    public function registerUser($email, $username) {
        $this->logger->log("注册新用户: $username ($email)");

        // 模拟用户注册
        echo "用户 $username 注册成功\n";

        // 发送欢迎邮件
        $this->emailService->sendEmail(
            $email,
            "欢迎注册",
            "欢迎 $username 加入我们的平台!"
        );

        return true;
    }
}

// 使用依赖注入
echo "=== 依赖注入示例 ===\n";

// 1. 创建基础服务
$logger = new Logger('user_service.log');
$emailService = new EmailService('smtp.example.com', $logger);

// 2. 将依赖注入到高级服务中
$userService = new UserService($emailService, $logger);

// 3. 使用服务
echo "\n=== 执行用户注册 ===\n";
$userService->registerUser('alice@example.com', 'Alice');
?>

构造函数重载(PHP不支持,但可通过默认参数和可选参数模拟)

<?php
class Order {
    private $orderId;
    private $customerName;
    private $items;
    private $totalAmount;
    private $orderDate;
    private $shippingAddress;
    private $discountCode;

    // 使用可选参数模拟重载
    public function __construct(
        $customerName,
        $items = [],
        $totalAmount = 0.0,
        $shippingAddress = null,
        $discountCode = null
    ) {
        $this->orderId = uniqid('order_');
        $this->customerName = $customerName;
        $this->items = $items;
        $this->totalAmount = $totalAmount;
        $this->orderDate = date('Y-m-d H:i:s');
        $this->shippingAddress = $shippingAddress ?: $customerName . ' 的默认地址';
        $this->discountCode = $discountCode;

        echo "创建订单 #$this->orderId\n";
        $this->applyDiscount();
    }

    private function applyDiscount() {
        if ($this->discountCode === 'SAVE10') {
            $this->totalAmount *= 0.9;
            echo "应用10%折扣\n";
        } elseif ($this->discountCode === 'SAVE20') {
            $this->totalAmount *= 0.8;
            echo "应用20%折扣\n";
        }
    }

    public function getInfo() {
        return sprintf(
            "订单 #%s | 客户:%s | 金额:¥%.2f | 地址:%s",
            substr($this->orderId, -8),
            $this->customerName,
            $this->totalAmount,
            $this->shippingAddress
        );
    }
}

// 模拟不同的构造方式
echo "=== 构造函数参数重载示例 ===\n";

// 方式1:最简单的订单
$order1 = new Order("张三");
echo $order1->getInfo() . "\n\n";

// 方式2:带商品的订单
$items = ["笔记本", "鼠标", "键盘"];
$order2 = new Order("李四", $items, 1500.00);
echo $order2->getInfo() . "\n\n";

// 方式3:完整的订单
$order3 = new Order(
    "王五",
    ["手机", "充电器", "耳机"],
    3999.00,
    "北京市朝阳区某某街道123号",
    "SAVE10"
);
echo $order3->getInfo() . "\n";
?>

析构函数(Destructor)

什么是析构函数

析构函数是在对象被销毁时自动调用的特殊方法。它的主要作用是:

  1. 清理资源:关闭文件句柄、数据库连接等
  2. 释放内存:清理大对象的内存
  3. 保存状态:保存对象的最终状态
  4. 执行清理逻辑:确保对象完全销毁前的清理工作

析构函数的语法

<?php
class MyClass {
    private $resource;

    public function __construct() {
        echo "构造函数:分配资源\n";
        $this->resource = "模拟资源";
    }

    // 析构函数
    public function __destruct() {
        echo "析构函数:清理资源\n";
        $this->resource = null;
    }
}
?>

基本析构函数示例

<?php
class TempFile {
    private $filename;
    private $handle;
    private $content;

    public function __construct($filename, $content = '') {
        echo "创建临时文件:$filename\n";

        $this->filename = $filename;
        $this->content = $content;

        // 模拟文件创建
        $this->handle = fopen("php://memory", "r+");  // 使用内存文件
        fwrite($this->handle, $content);
        rewind($this->handle);

        echo "文件句柄已创建\n";
    }

    public function write($data) {
        $this->content .= $data;
        fwrite($this->handle, $data);
        echo "写入数据:$data\n";
    }

    public function read() {
        rewind($this->handle);
        $content = stream_get_contents($this->handle);
        echo "读取文件内容:$content\n";
        return $content;
    }

    public function getSize() {
        return strlen($this->content);
    }

    public function __destruct() {
        echo "销毁临时文件:$this->filename\n";

        // 关闭文件句柄
        if (is_resource($this->handle)) {
            fclose($this->handle);
            echo "文件句柄已关闭\n";
        }

        // 在实际应用中,这里会删除临时文件
        // unlink($this->filename);
        echo "临时文件清理完成\n";
    }
}

// 使用析构函数
echo "=== 基本析构函数示例 ===\n";

function processTempFile() {
    echo "进入函数 processTempFile\n";
    $tempFile = new TempFile("temp_data.txt", "初始内容\n");
    $tempFile->write("添加的内容\n");
    $tempFile->read();
    echo "文件大小:{$tempFile->getSize()} 字节\n";
    echo "退出函数 processTempFile\n";
    // $tempFile 对象在这里会被销毁,自动调用 __destruct()
}

processTempFile();
echo "函数执行结束\n\n";

// 直接创建和销毁对象
echo "=== 直接创建对象 ===\n";
$tempFile2 = new TempFile("another_temp.txt", "另一个文件");
unset($tempFile2);  // 手动触发析构函数
echo "对象已被手动销毁\n";
?>

数据库连接的析构清理

<?php
class DatabaseConnection {
    private $connectionId;
    private $isConnected = false;
    private $queryCount = 0;

    public function __construct($database) {
        $this->connectionId = uniqid('db_');
        echo "建立数据库连接到:$database (ID: $this->connectionId)\n";

        // 模拟连接建立
        $this->isConnected = true;
        echo "数据库连接已建立\n";
    }

    public function query($sql) {
        if (!$this->isConnected) {
            throw new Exception("数据库未连接");
        }

        $this->queryCount++;
        echo "执行查询 #$this->queryCount: $sql\n";

        // 模拟查询执行
        return "查询结果";
    }

    public function getConnectionId() {
        return $this->connectionId;
    }

    public function getQueryCount() {
        return $this->queryCount;
    }

    public function __destruct() {
        echo "\n开始清理数据库连接 (ID: $this->connectionId)\n";

        if ($this->isConnected) {
            echo "关闭数据库连接...\n";
            $this->isConnected = false;

            // 在实际应用中,这里会关闭实际的数据库连接
            // $this->connection->close();

            echo "数据库连接已关闭\n";
            echo "总共执行了 $this->queryCount 个查询\n";
        }

        echo "数据库连接清理完成\n";
    }
}

// 使用数据库连接类
echo "=== 数据库连接析构示例 ===\n";

function performDatabaseOperations() {
    echo "=== 开始数据库操作 ===\n";

    $db = new DatabaseConnection("myapp_db");

    echo "\n执行一些查询...\n";
    $db->query("SELECT * FROM users");
    $db->query("SELECT * FROM products WHERE category = 'electronics'");
    $db->query("INSERT INTO orders (user_id, total) VALUES (1, 100.00)");

    echo "\n数据库操作完成,函数即将结束\n";
    // $db 对象在函数结束时会被销毁
}

performDatabaseOperations();

echo "\n函数已结束,数据库连接应该已被清理\n";
?>

对象引用与析构时机

<?php
class ResourceTracker {
    private $name;
    private static $activeObjects = [];

    public function __construct($name) {
        $this->name = $name;
        self::$activeObjects[] = $this;
        echo "创建资源:$name\n";
        $this->showActiveCount();
    }

    public function getName() {
        return $this->name;
    }

    public static function showActiveCount() {
        echo "当前活跃对象数:" . count(self::$activeObjects) . "\n";
    }

    public function __destruct() {
        echo "销毁资源:$this->name\n";

        // 从活跃对象列表中移除
        $key = array_search($this, self::$activeObjects, true);
        if ($key !== false) {
            unset(self::$activeObjects[$key]);
            self::$activeObjects = array_values(self::$activeObjects);
        }

        $this->showActiveCount();
    }
}

// 演示不同的对象销毁时机
echo "=== 对象引用与析构时机示例 ===\n";

// 1. 普通变量
echo "\n1. 普通变量作用域:\n";
{
    $obj1 = new ResourceTracker("对象1");
    $obj2 = new ResourceTracker("对象2");
    echo "离开代码块,对象将被销毁\n";
}
echo "已离开代码块\n\n";

// 2. 引用情况
echo "2. 对象引用:\n";
$obj3 = new ResourceTracker("对象3");
$reference = $obj3;  // 引用
echo "复制引用\n";
$anotherRef = $obj3;

echo "unset 第一个变量\n";
unset($obj3);
ResourceTracker::showActiveCount();

echo "unset 第二个变量\n";
unset($reference);
ResourceTracker::showActiveCount();

echo "unset 最后一个变量\n";
unset($anotherRef);
echo "对象应该已被销毁\n\n";

// 3. 数组中的对象
echo "3. 数组中的对象:\n";
$objects = [];
$objects[] = new ResourceTracker("数组对象1");
$objects[] = new ResourceTracker("数组对象2");
$objects[] = new ResourceTracker("数组对象3");

echo "清空数组\n";
$objects = [];  // 这会触发所有对象的析构函数
echo "数组已清空\n\n";

// 4. 函数参数传递
echo "4. 函数参数传递:\n";
function processObject($obj) {
    echo "函数内处理对象:{$obj->getName()}\n";
    echo "即将离开函数\n";
}

$obj4 = new ResourceTracker("函数对象");
processObject($obj4);
echo "函数结束,对象应该还存在\n";
echo "手动销毁对象\n";
unset($obj4);
?>

复杂的资源管理

<?php
class FileProcessor {
    private $inputFile;
    private $outputFile;
    private $tempFiles = [];
    private $processingSteps = [];

    public function __construct($inputFile, $outputFile) {
        echo "初始化文件处理器\n";
        echo "输入文件:$inputFile\n";
        echo "输出文件:$outputFile\n";

        $this->inputFile = $inputFile;
        $this->outputFile = $outputFile;

        // 模拟文件打开
        $this->openFiles();

        $this->logStep("初始化完成");
    }

    private function openFiles() {
        echo "打开输入文件:{$this->inputFile}\n";
        echo "创建输出文件:{$this->outputFile}\n";

        // 在实际应用中,这里会使用 fopen()
        $this->logStep("文件已打开");
    }

    public function processData($data) {
        $this->logStep("开始处理数据");

        // 创建临时文件
        $tempFile = $this->createTempFile($data);

        // 处理数据
        $processedData = $this->transformData($data);

        // 写入输出
        $this->writeOutput($processedData);

        // 清理临时文件
        $this->cleanupTempFile($tempFile);

        $this->logStep("数据处理完成");
        return $processedData;
    }

    private function createTempFile($data) {
        $tempFile = "temp_" . uniqid() . ".tmp";
        $this->tempFiles[] = $tempFile;
        echo "创建临时文件:$tempFile\n";

        // 在实际应用中,这里会写入实际文件
        $this->logStep("临时文件已创建:$tempFile");

        return $tempFile;
    }

    private function transformData($data) {
        echo "转换数据:$data\n";

        // 模拟数据处理
        $transformed = strtoupper($data) . " [已处理]";

        $this->logStep("数据转换完成");
        return $transformed;
    }

    private function writeOutput($data) {
        echo "写入输出文件:{$this->outputFile}\n";
        echo "内容:$data\n";

        $this->logStep("数据已写入输出文件");
    }

    private function cleanupTempFile($tempFile) {
        echo "清理临时文件:$tempFile\n";

        // 从列表中移除
        $key = array_search($tempFile, $this->tempFiles);
        if ($key !== false) {
            unset($this->tempFiles[$key]);
            $this->tempFiles = array_values($this->tempFiles);
        }

        // 在实际应用中,这里会删除实际文件
        $this->logStep("临时文件已清理:$tempFile");
    }

    private function logStep($step) {
        $timestamp = date('H:i:s');
        $this->processingSteps[] = "[$timestamp] $step";
        echo "  - $step\n";
    }

    public function getProcessingLog() {
        return $this->processingSteps;
    }

    public function __destruct() {
        echo "\n=== 析构函数:清理文件处理器 ===\n";

        // 关闭文件
        echo "关闭输入文件:{$this->inputFile}\n";
        echo "关闭输出文件:{$this->outputFile}\n";

        // 清理所有剩余的临时文件
        if (!empty($this->tempFiles)) {
            echo "清理剩余的临时文件:\n";
            foreach ($this->tempFiles as $tempFile) {
                echo "  - 删除 $tempFile\n";
                // unlink($tempFile);
            }
            $this->tempFiles = [];
        }

        // 显示处理日志
        if (!empty($this->processingSteps)) {
            echo "\n处理步骤总结:\n";
            foreach ($this->processingSteps as $i => $step) {
                echo "  " . ($i + 1) . ". $step\n";
            }
        }

        echo "文件处理器清理完成\n";
    }
}

// 使用复杂的资源管理
echo "=== 复杂资源管理示例 ===\n";

function processUserData() {
    $processor = new FileProcessor("input.txt", "output.txt");

    echo "\n开始处理用户数据...\n";
    $processor->processData("Hello World");
    $processor->processData("PHP Programming");
    $processor->processData("File Processing");

    echo "\n用户数据处理完成\n";
    // $processor 在函数结束时会被销毁
}

processUserData();
echo "函数执行结束,所有资源应该已被清理\n";
?>

实际应用示例

配置管理器

<?php
class ConfigManager {
    private $configFile;
    private $config = [];
    private $isModified = false;
    private $autoSave;

    public function __construct($configFile, $autoSave = true) {
        echo "初始化配置管理器\n";
        echo "配置文件:$configFile\n";
        echo "自动保存:" . ($autoSave ? "启用" : "禁用") . "\n";

        $this->configFile = $configFile;
        $this->autoSave = $autoSave;

        $this->loadConfig();
    }

    private function loadConfig() {
        echo "加载配置文件...\n";

        // 在实际应用中,这里会读取JSON、INI等配置文件
        // 模拟配置加载
        $this->config = [
            'database' => [
                'host' => 'localhost',
                'port' => 3306,
                'name' => 'myapp'
            ],
            'app' => [
                'debug' => false,
                'timezone' => 'Asia/Shanghai'
            ]
        ];

        echo "配置加载完成,共 " . count($this->config, COUNT_RECURSIVE) . " 个配置项\n";
    }

    public function get($key, $default = null) {
        $keys = explode('.', $key);
        $value = $this->config;

        foreach ($keys as $k) {
            if (isset($value[$k])) {
                $value = $value[$k];
            } else {
                return $default;
            }
        }

        return $value;
    }

    public function set($key, $value) {
        echo "设置配置:$key = " . (is_bool($value) ? ($value ? 'true' : 'false') : $value) . "\n";

        $keys = explode('.', $key);
        $config = &$this->config;

        for ($i = 0; $i < count($keys) - 1; $i++) {
            if (!isset($config[$keys[$i]])) {
                $config[$keys[$i]] = [];
            }
            $config = &$config[$keys[$i]];
        }

        $config[$keys[count($keys) - 1]] = $value;
        $this->isModified = true;
    }

    public function has($key) {
        return $this->get($key) !== null;
    }

    public function remove($key) {
        if ($this->has($key)) {
            echo "删除配置:$key\n";
            // 实际实现中需要处理嵌套键
            $this->isModified = true;
        }
    }

    public function save() {
        if (!$this->isModified) {
            echo "配置未修改,无需保存\n";
            return;
        }

        echo "保存配置到文件:{$this->configFile}\n";

        // 在实际应用中,这里会写入文件
        $this->isModified = false;
        echo "配置保存成功\n";
    }

    public function getConfig() {
        return $this->config;
    }

    public function __destruct() {
        echo "\n=== 配置管理器析构 ===\n";

        if ($this->autoSave && $this->isModified) {
            echo "检测到配置修改,自动保存...\n";
            $this->save();
        } else {
            echo "配置管理器关闭(修改的配置:" . ($this->isModified ? "是" : "否") . ")\n";
        }
    }
}

// 使用配置管理器
echo "=== 配置管理器示例 ===\n";

function configureApp() {
    $config = new ConfigManager("app.conf");

    echo "\n读取配置:\n";
    echo "数据库主机:" . $config->get('database.host') . "\n";
    echo "调试模式:" . ($config->get('app.debug', false) ? '开启' : '关闭') . "\n";

    echo "\n修改配置:\n";
    $config->set('app.debug', true);
    $config->set('database.charset', 'utf8mb4');

    echo "\n读取新配置:\n";
    echo "调试模式:" . ($config->get('app.debug') ? '开启' : '关闭') . "\n";
    echo "数据库字符集:" . $config->get('database.charset', '未设置') . "\n";

    // 函数结束时,由于配置被修改且启用了自动保存,会自动触发保存
}

configureApp();
echo "应用配置完成\n\n";

// 禁用自动保存的配置管理器
echo "=== 禁用自动保存 ===\n";
$config2 = new ConfigManager("user.conf", false);
$config2->set('user.name', 'Alice');
$config2->set('user.email', 'alice@example.com');

echo "手动保存配置\n";
$config2->save();

echo "手动销毁对象\n";
unset($config2);
?>

对象池模式

<?php
class ObjectPool {
    private static $pools = [];
    private $poolId;
    private $createdObjects = [];
    private $borrowedObjects = [];
    private $maxSize;

    public function __construct($poolId, $maxSize = 10) {
        $this->poolId = $poolId;
        $this->maxSize = $maxSize;

        self::$pools[$poolId] = $this;

        echo "创建对象池:$poolId (最大容量:$maxSize)\n";
    }

    public function createObject($className, ...$args) {
        $objectId = uniqid();

        echo "创建对象:$className (ID: $objectId)\n";

        // 在实际应用中,这里会使用反射创建对象
        $object = (object)[
            'id' => $objectId,
            'class' => $className,
            'args' => $args,
            'created_at' => time()
        ];

        $this->createdObjects[$objectId] = $object;
        $this->returnToPool($object);

        return $objectId;
    }

    public function borrowObject() {
        if (!empty($this->createdObjects)) {
            $objectId = key($this->createdObjects);
            $object = array_shift($this->createdObjects);
            $this->borrowedObjects[$objectId] = $object;

            echo "借用对象:$objectId\n";
            return $object;
        }

        echo "池中无可用对象\n";
        return null;
    }

    public function returnToPool($object) {
        $objectId = $object->id;

        if (isset($this->borrowedObjects[$objectId])) {
            unset($this->borrowedObjects[$objectId]);
        }

        if (count($this->createdObjects) < $this->maxSize) {
            $this->createdObjects[$objectId] = $object;
            echo "对象归还到池:$objectId\n";
        } else {
            echo "池已满,丢弃对象:$objectId\n";
        }
    }

    public function getPoolInfo() {
        return [
            'pool_id' => $this->poolId,
            'pool_size' => count($this->createdObjects),
            'borrowed_count' => count($this->borrowedObjects),
            'max_size' => $this->maxSize
        ];
    }

    public static function getPool($poolId) {
        return self::$pools[$poolId] ?? null;
    }

    public function __destruct() {
        echo "\n=== 销毁对象池:{$this->poolId} ===\n";

        // 清理所有对象
        $totalObjects = count($this->createdObjects) + count($this->borrowedObjects);
        echo "清理池中的对象:$totalObjects 个\n";

        $this->createdObjects = [];
        $this->borrowedObjects = [];

        // 从全局池注册中移除
        unset(self::$pools[$this->poolId]);

        echo "对象池 {$this->poolId} 已销毁\n";
    }
}

// 使用对象池
echo "=== 对象池示例 ===\n";

function useObjectPool() {
    // 创建对象池
    $pool = new ObjectPool("user_pool", 5);

    echo "\n创建一些对象:\n";
    $pool->createObject("User", "Alice");
    $pool->createObject("User", "Bob");
    $pool->createObject("User", "Charlie");

    echo "\n当前池状态:\n";
    $info = $pool->getPoolInfo();
    echo "池大小:{$info['pool_size']}\n";
    echo "借用数量:{$info['borrowed_count']}\n";

    echo "\n借用和归还对象:\n";
    $obj1 = $pool->borrowObject();
    $obj2 = $pool->borrowObject();

    $info = $pool->getPoolInfo();
    echo "借用后池大小:{$info['pool_size']}\n";

    $pool->returnToPool($obj1);
    $info = $pool->getPoolInfo();
    echo "归还后池大小:{$info['pool_size']}\n";

    // 函数结束时,对象池会被销毁
}

useObjectPool();
echo "对象池使用完成\n";
?>

最佳实践和注意事项

构造函数最佳实践

  1. 保持简单:构造函数应该尽可能简单,只执行必要的初始化
  2. 避免抛出异常:尽量在构造函数中避免复杂的逻辑和可能抛出异常的操作
  3. 使用依赖注入:通过构造函数注入依赖,提高代码的可测试性
  4. 参数验证:验证传入的参数,确保对象处于有效状态
  5. 文档说明:清晰说明构造函数的参数和要求
<?php
// 好的构造函数设计
class UserService {
    private $userRepository;
    private $emailService;
    private $logger;

    public function __construct(
        UserRepositoryInterface $userRepository,
        EmailServiceInterface $emailService,
        LoggerInterface $logger
    ) {
        $this->userRepository = $userRepository;
        $this->emailService = $emailService;
        $this->logger = $logger;

        $this->logger->info('UserService 已初始化');
    }

    // 其他方法...
}
?>

析构函数最佳实践

  1. 快速执行:析构函数应该快速执行,避免耗时操作
  2. 避免异常:不要在析构函数中抛出异常
  3. 清理资源:确保清理所有打开的资源
  4. 不要依赖执行时机:析构函数的执行时机不确定
  5. 使用引用计数:了解PHP的引用计数垃圾回收机制
<?php
// 好的析构函数设计
class DatabaseConnection {
    private $connection;
    private $isConnected = false;

    public function __construct($config) {
        $this->connection = $this->connect($config);
        $this->isConnected = true;
    }

    public function __destruct() {
        if ($this->isConnected && $this->connection) {
            // 快速、安全的清理操作
            $this->connection->close();
            $this->isConnected = false;
        }
    }

    // 提供显式关闭方法
    public function close() {
        if ($this->isConnected && $this->connection) {
            $this->connection->close();
            $this->isConnected = false;
        }
    }
}
?>

性能考虑

  1. 避免在析构函数中执行耗时操作
  2. 及时释放大对象的内存
  3. 考虑使用显式关闭方法而不是依赖析构函数
  4. 理解PHP的垃圾回收机制

总结

构造函数和析构函数是面向对象编程中的重要组成部分:

关键要点:

  1. 构造函数

    • 在对象创建时自动调用
    • 用于初始化对象属性和分配资源
    • 支持参数传递和依赖注入
    • 是对象生命周期的起点
  2. 析构函数

    • 在对象销毁时自动调用
    • 用于清理资源和释放内存
    • 执行时机不确定,由垃圾回收机制决定
    • 是对象生命周期的终点
  3. 对象生命周期

    • 创建 → 构造函数执行 → 使用 → 销毁 → 析构函数执行
    • 理解引用计数和垃圾回收机制
    • 合理管理对象的生命周期
  4. 资源管理

    • 在构造函数中分配资源
    • 在析构函数中释放资源
    • 遵循"谁分配,谁释放"的原则

最佳实践:

  • 构造函数保持简单,只执行必要的初始化
  • 析构函数快速执行,避免耗时操作
  • 使用依赖注入提高代码的可测试性
  • 及时清理资源,避免内存泄漏
  • 理解PHP的垃圾回收机制

通过合理使用构造函数和析构函数,你可以构建更加健壮和可靠的面向对象应用程序。