PHP Conference Japan 2024

Serializable 接口

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

简介

自定义序列化的接口。

实现此接口的类将不再支持 __sleep()__wakeup() 。 不论何时,只要有实例需要被序列化, serialize 方法都将被调用。它不会调用 __destruct(),除非在该方法中编写了相关功能,否则它也不会有副作用(side effect)。 当数据被反序列化时,类将被感知并且调用合适的 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 notes

up
85
grzeniufication
9 years ago
Here's an example how to un-, serialize more than one property:

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

}
up
12
shaun at slickdesign dot com dot au
6 years ago
Serialized strings differ between instances that implement Serializable and those that don't.

Instances that don't implement Serializable use the Object notation "O:" when serialized, while those that do use the Class notation "C:". Class notation can only be used to unserialize instances that implement Serializable, while the Object notation can be used to unserialize any object.

Because of this, it is sometimes useful to implement the __wakeup() function when implementing Serializable, for instances where you may have a copy of the serialised class before it implemented Serializable (backwards compatible), or when you're expecting a serialized object from an external source, and they use Object notation for maximum compatibility. You can also use __wakeup() to process your unserialize function, or use it to help prevent people trying to bypass your unserialize.

Below is an example of a simple class hierarchy, where A is a standard class, B implements Serializable, and C uses __wakeup() to assist with unserializing it.

<?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;}"
?>
Class A outputs the following object, and its serialized string uses the object notation "O:". Please note that there is a null byte "\0" either side of the star*.

Changing the serialised string and unserializing it can cause protected and private values to change.
<?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)
//}
?>

Class B extends A, and so has the same constructor and properties. It also implements 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;}
?>
As well as being a lot shorter, the serialized string uses the Class notation "C:", but you can still unserialize it using the older style notation. Doing this however will completely ignore the unserialize() function, potentially update the wrong information, and the function do_extra_processing_here() from the example above is not called.
<?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)
//}
?>

Class C extends B, so it's already using the serialize() and unserialize() functions. By implementing the __wakeup() method, we ensure that we are validating the information and performing our do_extra_processing_here() function.
<?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)
//}
?>
We can use __wakeup() to revert our readonly data back to what it was, or to add additional processing. You can additionally call __wakeup() from within unserialize() if you need to do the same process regardless of which serialized string notation was used.
up
8
info at ensostudio dot ru
4 years ago
Note: that interface declared as "deprecated" in PHP 7.4, use magic methods __serialize() and __unserialize() instead .
up
2
marcos dot gottardi at folha dot REM0VE-THIS dot com dot br
12 years ago
Serializing child and parent classes:

<?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;

?>

This will output:

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