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"

目录

添加说明

用户贡献说明 7 说明

83
grzeniufication
8 年前
以下是如何反序列化多个属性的示例

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);
}

}
11
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()。
7
info at ensostudio dot ru
3 年前
注意:该接口在 PHP 7.4 中被声明为 “已弃用”,请改用魔术方法 __serialize() 和 __unserialize()。
1
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
My class name
My data
-10
Olivier Pons
10 年前
这里是你如何实现可序列化,以便 *所有* 后代都能序列化自己,而无需为所有后代重新编写 serialize() 和 unserialize() 函数。

注意:这只会序列化 “可见” 属性,它不会序列化私有的后代属性。如果你不希望序列化后代的属性,请将其设置为私有。

class Pot implements Serializable
{
protected $_a;
protected $_b;

public function serialize()
{
return serialize(get_object_vars($this));
}
public function unserialize($data)
{
$values = unserialize($data);
foreach ($values as $key=>$value) {
$this->$key = $value;
}
}
}

现在是一个后代

class PotId implements Pot
{
protected $_k;
}

class Pots implements PotId
{
protected $_l;
}

$pots = new Pots();

调用 serialize($pots) 将序列化所有属性 ($_a, $_b, $_k, $l)。
-7
Wojciech Brozyna
8 年前
我的解决方案可以让你序列化继承的类。
无需指定要序列化的内容。
在这个示例中,我们想要摆脱 PDO 对象。

abstract class DefaultModel() implements \Serializable
{

/**
* @var PDO
*/
private $pdo;

/**
* 序列化对象
*
* @return string
*/
public function serialize()
{

$serializable = get_object_vars($this);

// 取消设置保存 PDO 实例的属性名称
unset($serializable['pdo']);

return serialize($serializable);

}

/**
* 反序列化对象
*
* @param string $serialized 序列化的对象
* @return DefaultModel
*/
public function unserialize($serialized)
{

$unserialized = unserialize($serialized);

// 重新创建 PDO 对象
$this->pdo = $this->createDBObject();

if(is_array($unserialized) === true) {

foreach($unserialized as $property => $value) {

$this->{$property} = $value;

}

}

}

}

class RealModel extends DefaultModel
{

private $myVar;

public function setMyVar($value)
{

$this->myVar = $value;

}

}

$model = new RealModel();
$model->setMyVar('123456');

$serialized = serialize($model);
$serialized = $model->serialize(); // 也将正常工作

print_r($serialized);

$unserialized = unserialize($serialized);

print_r($unserialized);

希望这有帮助。
-19
Anonymous
13 年前
你可以通过返回 NULL 来阻止对象被反序列化。PHP 将返回 NULL 的序列化形式,而不是序列化的对象。

<?php
class testNull implements Serializable {
public function
serialize() {
return
NULL;
}
public function
unserialize($data) {
}
}

$obj = new testNull;
$string = serialize($obj);
echo
$string; // "N;"
?>

如果你想阻止某些对象的序列化,这可能比在 serialize 函数中抛出异常更好。
To Top