Menu
CoddyTech

__serialize 和 __unserialize

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 49 课,共 91 课。

当你需要将对象转换为字符串以便存储或传输时,PHP 会使用序列化。__serialize() 和 __unserialize() 魔术方法让你可以控制具体保存哪些数据,以及如何还原这些数据。

__serialize() 方法返回要序列化的数据数组,而 __unserialize() 接收该数组并重建对象:

<?php
class Session {
    public function __construct(
        private string $userId,
        private array $data,
        private int $createdAt
    ) {}
    
    public function __serialize(): array {
        return [
            'userId' => $this->userId,
            'data' => $this->data
        ];
    }
    
    public function __unserialize(array $data): void {
        $this->userId = $data['userId'];
        $this->data = $data['data'];
        $this->createdAt = time();
    }
    
    public function getCreatedAt(): int {
        return $this->createdAt;
    }
}

$session = new Session("user123", ["theme" => "dark"], time());
$serialized = serialize($session);

$restored = unserialize($serialized);
echo "Created at: " . $restored->getCreatedAt();

注意,createdAt 不会被序列化,并且在反序列化时会获得一个新的时间戳。这对于排除敏感数据、临时值或无法序列化的资源(例如数据库连接)很有用。

这些方法在 PHP 7.4 中取代了旧的 __sleep() 和 __wakeup() 方法,让你可以直接处理数据数组,而不是属性名称,方式也更简洁。

challenge icon

挑战

简单

让我们构建一个用户资料缓存系统,演示 __serialize() 和 __unserialize() 如何让你控制要保存哪些数据,以及如何还原这些数据。

你需要将代码分布在两个文件中:

  • UserProfile.php:创建一个 UserProfile 类,用于表示具有缓存功能的用户资料。使用构造函数属性提升来定义以下私有属性:$userId(字符串)、$username(字符串)、$email(字符串)和 $accessToken(字符串)。添加一个私有的 $cachedAt 属性,用于存储资料缓存的时间(在构造函数中将其初始值设为 "not cached")。类中需要包含:
    • __serialize():返回一个数组,其中只包含 userId、username 和 email。accessToken 是敏感信息,绝不能序列化。还原时会重新生成 cachedAt 时间戳。
    • __unserialize():从数据数组中还原 userId、username 和 email。将 accessToken 设为 "expired"(因为令牌不应持久保存)。
    • getInfo():返回符合以下格式的字符串:"[username] ([email]) - Token: [accessToken] - Cached: [cachedAt]"
  • main.php:引入 UserProfile 文件。你会收到 4 个输入:用户 ID、用户名、电子邮件和访问令牌。使用这些值创建一个 UserProfile 实例。打印 3 行:
    • 原始资料的信息(序列化之前)
    • 使用 serialize() 序列化资料,然后使用 unserialize() 反序列化,创建还原后的资料。打印还原后资料的信息。
    • 根据还原后资料的令牌是否与原始令牌匹配,打印 "Token preserved: yes" 或 "Token preserved: no"(提示:令牌在还原后会过期,因此两者不应匹配)

这种模式对于安全地缓存用户数据至关重要。你需要存储资料信息,但绝不持久保存访问令牌等敏感凭据。从缓存还原资料时,令牌会被标记为已过期,迫使应用获取一个新的令牌。

亲手试试

<?php

require_once 'UserProfile.php';

// 读取输入
$userId = trim(fgets(STDIN));
$username = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$accessToken = trim(fgets(STDIN));

// TODO: 用输入值创建一个 UserProfile 实例

// TODO: 输出原始资料的信息(序列化之前)

// TODO: 先序列化资料,再反序列化,创建恢复后的资料

// TODO: 输出恢复后的资料信息

// TODO: 输出 "Token preserved: yes" 或 "Token preserved: no"
// 取决于恢复后的资料中的 token 是否与原始资料中的一致

?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器