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 {
/* 方法 */
公開 serialize(): ?字串
公開 unserialize(字串 $data):
}

範例

範例 #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
{
受保護的 $property1;
受保護的 $property2;
受保護的 $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 的實例在序列化時使用物件表示法「O:」,而實作的實例則使用類別表示法「C:」。類別表示法只能用於反序列化實作 Serializable 的實例,而物件表示法則可以用於反序列化任何物件。

因此,在實作 Serializable 時,有時實作 __wakeup() 函式會很有用,例如當您可能擁有在類別實作 Serializable 之前的序列化副本(向後相容)時,或者當您預期來自外部來源的序列化物件,並且它們使用物件表示法以獲得最大相容性時。您也可以使用 __wakeup() 來處理您的 unserialize 函式,或使用它來防止人們試圖繞過您的 unserialize。

以下是一個簡單類別階層的範例,其中 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;

?>

這將輸出

正在序列化 MyChildClass...
正在序列化 MyClass...
正在反序列化 MyChildClass...
正在反序列化 MyClass...
15
我的類別名稱
我的資料
To Top