__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() 方法,让你可以直接处理数据数组,而不是属性名称,方式也更简洁。
挑战
简单让我们构建一个用户资料缓存系统,演示 __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 是否与原始资料中的一致
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
8魔术方法
魔术方法简介__toString 和 __debugInfo__get、__set、__isset、__unset__call 和 __callStatic__clone 和对象克隆__serialize 和 __unserialize回顾:自定义集合自己练习: PHP 在线编译器