PHP Conference Japan 2024

Serializable 接口

(PHP 5 >= 5.1.0,PHP 7,PHP 8)

简介

用于自定义序列化的接口。

实现此接口的类不再支持__sleep()__wakeup()。每当需要序列化实例时,都会调用 serialize 方法。除非在方法内部编程,否则不会调用 __destruct() 或者有任何其他副作用。当数据被反序列化时,类是已知的,并且适当的 unserialize() 方法被调用作为构造函数,而不是调用 __construct()。如果您需要执行标准构造函数,您可以在方法中执行。

警告

从 PHP 8.1.0 开始,一个实现了Serializable 但没有同时实现__serialize()__unserialize() 的类将生成弃用警告。

接口概要

interface Serializable {
/* 方法 */
public serialize(): ?string
public unserialize(string $data): void
}

示例

示例 #1 基本用法

<?php
class obj implements Serializable {
private
$data;
public function
__construct() {
$this->data = "My private data";
}
public function
serialize() {
return
serialize($this->data);
}
public function
unserialize($data) {
$this->data = unserialize($data);
}
public function
getData() {
return
$this->data;
}
}

$obj = new obj;
$ser = serialize($obj);

var_dump($ser);

$newobj = unserialize($ser);

var_dump($newobj->getData());
?>

以上示例将输出类似以下内容

string(38) "C:3:"obj":23:{s:15:"My private data";}"
string(15) "My private data"

目录

添加注释

用户贡献的注释 4 条注释

grzeniufication
9 年前
这是一个关于如何反序列化和序列化多个属性的示例

class Example implements \Serializable
{
protected $property1;
protected $property2;
protected $property3;

public function __construct($property1, $property2, $property3)
{
$this->property1 = $property1;
$this->property2 = $property2;
$this->property3 = $property3;
}

public function serialize()
{
return serialize([
$this->property1,
$this->property2,
$this->property3,
]);
}

public function unserialize($data)
{
list(
$this->property1,
$this->property2,
$this->property3
) = unserialize($data);
}

}
shaun at slickdesign dot com dot au
6 年前
实现 Serializable 的实例和未实现 Serializable 的实例之间的序列化字符串不同。

未实现 Serializable 的实例在序列化时使用对象表示法“O:” ,而实现 Serializable 的实例则使用类表示法“C:”。类表示法只能用于反序列化实现 Serializable 的实例,而对象表示法可以用于反序列化任何对象。

因此,在实现 Serializable 时,有时使用 __wakeup() 函数很有用,例如当您在类实现 Serializable 之前拥有该类的序列化副本(向后兼容)时,或者当您期望来自外部源的序列化对象并且它们使用对象表示法以实现最大兼容性时。您还可以使用 __wakeup() 来处理反序列化函数,或者使用它来帮助防止人们尝试绕过您的反序列化。

下面是一个简单的类层次结构示例,其中 A 是一个标准类,B 实现 Serializable,而 C 使用 __wakeup() 来协助反序列化。

<?php
class A {
protected
$readonly_data = true;
public
$public_data = true;

public function
__construct( $data = true ) {
$this->public_data = $data;
}

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

$a = new A;

var_dump( $a );
//object(A)#1 (2) {
// ["readonly_data":protected]=>
// bool(true)
// ["public_data"]=>
// bool(true)
//}
var_dump( serialize( $a ) );
//string(63) "O:1:"A":2:{s:16:"*readonly_data";b:1;s:11:"public_data";b:1;}"
?>
类 A 输出以下对象,其序列化字符串使用对象表示法“O:”。请注意,星号*的两侧都有一个空字节“\0”。

更改序列化字符串并对其进行反序列化会导致受保护和私有值发生变化。
<?php
var_dump
( unserialize( "O:1:\"A\":2:{s:16:\"\0*\0readonly_data\";b:0;s:11:\"public_data\";b:0;}" ) );
//object(A)#1 (2) {
// ["readonly_data":protected]=>
// bool(false)
// ["public_data"]=>
// bool(false)
//}
?>

类 B 扩展 A,因此具有相同的构造函数和属性。它还实现了 Serializable。
<?php
class B extends A implements Serializable {
public function
serialize() {
return
serialize( $this->public_data );
}

public function
unserialize( $data ) {
$this->public_data = unserialize ( $data );
do_extra_processing_here();
}
}

$b = new B;

var_dump( serialize( $b ) );
// C:1:"B":4:{b:1;}
?>
除了长度更短之外,序列化后的字符串使用了“C:”的类表示法,但您仍然可以使用旧样式的表示法对其进行反序列化。但是,这样做将完全忽略`unserialize()`函数,可能会更新错误的信息,并且上面示例中的`do_extra_processing_here()`函数不会被调用。
<?php
var_dump
( unserialize( "O:1:\"B\":2:{s:16:\"\0*\0readonly_data\";b:0;s:11:\"public_data\";b:0;}" ) );
//object(B)#1 (2) {
// ["readonly_data":protected]=>
// bool(false)
// ["public_data"]=>
// bool(false)
//}
?>

类C扩展了B,因此它已经使用了`serialize()`和`unserialize()`函数。通过实现`__wakeup()`方法,我们可以确保我们正在验证信息并执行`do_extra_processing_here()`函数。
<?php
class C extends B {
public function
__wakeup() {
$new = new static;
$this->readonly_data = $new->get_readonly_data();
do_extra_processing_here();
}
}

var_dump( unserialize( "O:1:\"C\":2:{s:16:\"\0*\0readonly_data\";b:0;s:11:\"public_data\";b:0;}" ) );
//object(B)#1 (2) {
// ["readonly_data":protected]=>
// bool(true)
// ["public_data"]=>
// bool(false)
//}
?>
我们可以使用`__wakeup()`将我们的只读数据恢复到原来的状态,或者添加额外的处理。如果需要无论使用哪种序列化字符串表示法都执行相同的过程,还可以从`unserialize()`内部调用`__wakeup()`。
info at ensostudio dot ru
4年前
注意:在PHP 7.4中,该接口被声明为“已弃用”,请改用魔术方法`__serialize()`和`__unserialize()`。
marcos dot gottardi at folha dot REM0VE-THIS dot com dot br
12年前
序列化子类和父类

<?php
class MyClass implements Serializable {
private
$data;

public function
__construct($data) {
$this->data = $data;
}

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

public function
serialize() {
echo
"Serializing MyClass...\n";
return
serialize($this->data);
}

public function
unserialize($data) {
echo
"Unserializing MyClass...\n";
$this->data = unserialize($data);
}
}

class
MyChildClass extends MyClass {
private
$id;
private
$name;

public function
__construct($id, $name, $data) {
parent::__construct($data);
$this->id = $id;
$this->name = $name;
}

public function
serialize() {
echo
"Serializing MyChildClass...\n";
return
serialize(
array(
'id' => $this->id,
'name' => $this->name,
'parentData' => parent::serialize()
)
);
}

public function
unserialize($data) {
echo
"Unserializing MyChildClass...\n";
$data = unserialize($data);

$this->id = $data['id'];
$this->name = $data['name'];
parent::unserialize($data['parentData']);
}

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

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

$obj = new MyChildClass(15, 'My class name', 'My data');

$serial = serialize($obj);
$newObject = unserialize($serial);

echo
$newObject->getId() . PHP_EOL;
echo
$newObject->getName() . PHP_EOL;
echo
$newObject->getData() . PHP_EOL;

?>

这将输出

Serializing MyChildClass...
Serializing MyClass...
Unserializing MyChildClass...
Unserializing MyClass...
15
15
My class name
To Top