PHP serialize 序列化完全指南

PHP serialize 序列化完全指南

介绍

如果你和我一样,第一次在 PHP 中看到序列化字符串时会觉得很困惑。我当时在做一个 Laravel 项目,想搞清楚将任务推送到队列时到底发生了什么。我发现一些数据被序列化了,但不知道为什么以及怎么工作的。不过在我花时间研究序列化后,发现它其实没那么复杂。

本文会介绍什么是序列化以及工作原理。然后会说明如何使用 PHP 的内置序列化函数,让你能在应用中序列化和反序列化数据。最后会讲如何编写测试来确保序列化代码正常工作。

读完这篇文章,你应该能理解什么是序列化,并且能放心地在项目中使用。

什么是序列化?

序列化就是把变量、对象或数据结构转换成字符串格式的过程。这种字符串格式能表示原始数据,方便存储或传输。反过来,反序列化(在 PHP 中通常叫"unserialization")就是把序列化的数据转换回原来的形式。

序列化很重要,常用于把数据存储到缓存、数据库或文件中。

数据可以序列化成很多格式,比如 JSON、XML,甚至二进制格式(比如 gRPC API 用的 Protocol Buffers)。不过这篇文章主要讲 PHP 的内置序列化函数。

举个例子,如果你用过 Laravel,应该注意到这个框架在把任务推送到队列时会序列化数据。比如下面这个 Laravel 中被推到队列的待处理任务(为了好看,分了行并去掉了一些属性):

{
   
   
    "uuid": "3d05be68-8cd0-4c3a-8d05-71e86871713a",
    "data": {
   
   
        "commandName": "App\\Jobs\\SendOneTimePassword",
        "command": "O:28:\"App\\Jobs\\SendOneTimePassword\"
              :1:{
   
   s:15:\"oneTimePassword\";s:6:\"123456\";}"
    }
}

在这个待处理任务的 JSON 例子中,data.command 属性是个序列化字符串,代表一个 App\Jobs\SendOneTimePassword 任务。队列工作器拿到这个任务时,会把序列化字符串反序列化,创建 App\Jobs\SendOneTimePassword 类的实例来处理。如果现在看不懂也没关系,后面会有更多例子来解释。

PHP 中的序列化如何工作?

PHP 中可以用 serializeunserialize 函数来序列化和反序列化数据。

serialize 函数接受要序列化的数据,返回字符串格式。unserialize 函数接受序列化的数据,返回原来的数据结构。

看看怎么在 PHP 中序列化和反序列化不同类型的数据:

序列化字符串

序列化字符串很简单,直接传给 serialize 函数:

$serialized = serialize('Hello');

这将返回一个序列化字符串:

s:5:"Hello";

这乍看起来有点奇怪,但一旦你注意到模式,你会发现它并不像看起来那么可怕。我们的序列化数据遵循格式:data_type:string_length:string;

所以在上面序列化字符串的情况下,s 代表字符串并表示反序列化数据时的数据类型,5 是字符串的长度。

然后我们可以将该序列化字符串传递给 unserialize 函数以获取原始字符串:

$string = unserialize('s:5:"Hello";');

序列化整数和浮点数

我们也可以在 PHP 中序列化整数和浮点数。以下是序列化整数的方法:

serialize(123);

这将返回一个序列化字符串:

i:123;

你可能已经注意到结构与我们之前看到的序列化字符串略有不同。整数使用格式 data_type:data; 进行序列化。注意这里我们没有像字符串那样的大小。在这种情况下,序列化数据的数据类型是 i 表示整数。

同样,我们可以序列化浮点数:

serialize(123.45);

这将返回一个序列化字符串:

d:123.45;

这个结构类似于整数序列化,但数据类型是 d 表示双精度浮点数。

序列化布尔值

我们也可以在 PHP 中序列化布尔值。例如,我们可以序列化 true

serialize(true);

这将返回一个序列化字符串,其中 b 作为数据类型,1(表示 true)作为值:

b:1;

同样,我们可以序列化 false

serialize(false);

这将返回一个序列化字符串,其中 b 作为数据类型,0(表示 false)作为值:

"b:0;"

序列化数组

我们可以这样在 PHP 中序列化数组:

serialize([1,2,3]);

这将返回一个序列化字符串:

a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}

现在,你可能已经注意到这比我们已经看过的其他序列化数据要复杂一些。让我们分解一下。

字符串具有 data_type:size:{key_data_type:key_data;value_data_type:value_data;...} 的结构。在这种情况下,data_typea 表示数组,size3,因为数组有 3 个元素。

如果我们然后查看 { } 内的数据,我们可以看到键由 i 表示整数,值也由 i 表示整数。通过将它们分成新行来可视化结构可能会有所帮助:

i:0;i:1;
i:1;i:2;
i:2;i:3;

作为另一个例子,让我们看看序列化的字符串数组可能是什么样子。我们可以序列化以下数组:

serialize(['a','b','c']);

这将返回一个序列化字符串:

a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}

正如我们在上面的序列化字符串中看到的,键仍然由 i 表示,而值由 s 表示字符串。为了帮助可视化结构,我们可以将数据分成新行:

i:0;s:1:"a";
i:1;s:1:"b";
i:2;s:1:"c";

同样,我们也可以序列化关联数组:

serialize(['a' => 'A', 'b' => 'B', 'c' => 'C']);

这将返回一个序列化字符串:

a:3:{s:1:"a";s:1:"A";s:1:"b";s:1:"B";s:1:"c";s:1:"C";}

正如我们所看到的,结构与我们已经看过的序列化数组非常相似。但是,在这种情况下,键由 s 表示字符串。为了帮助可视化结构,我们可以将数据分成新行:

s:1:"a";s:1:"A";
s:1:"b";s:1:"B";
s:1:"c";s:1:"C";

序列化枚举

我们也可以在 PHP 中序列化枚举。作为一个基本示例,假设我们有以下表示博客文章状态的枚举:

namespace App\Enums;

enum PostStatus: string
{
   
   
    case Published = 'published';
    case Draft = 'draft';
    case Pending = 'in_review';
}

让我们想象然后创建此枚举的新实例并像这样序列化它:

serialize(PostStatus::Published);

这将返回一个序列化字符串:

E:30:"App\Enums\PostStatus:Published";

序列化枚举的结构是 data_type:size:"enum_type:enum_value";。在这种情况下,数据类型由 E 表示,大小是 30,因为类名是 App\Enums\PostStatus,枚举值是 Published

序列化对象

到目前为止,我们已经介绍了序列化如何适用于基本数据类型,如字符串、整数、浮点数、布尔值、数组和枚举。但是对象呢?

默认情况下,除了少数内置 PHP 类之外,所有对象都是可序列化的。

为了解释对象序列化的工作原理,让我们以一个基本的 App\User 类为例,它包含三个公共属性:

namespace App;

class User
{
   
   
    public function __construct(
        public string $name,
        public 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值