1. 程式人生 > 其它 >PHP 物件序列化

PHP 物件序列化

1. 物件序列化

1.1 serialize() 和 unserialize()

所有php裡面的值都可以使用函式serialize()來返回一個包含位元組流的字串來表示。unserialize()函式能夠重新把字串變回php原來的值。 序列化一個物件將會儲存物件的所有變數,但是不會儲存物件的方法,只會儲存類的名字。

為了能夠unserialize()一個物件,這個物件的類必須已經定義過。如果序列化類A的一個物件,將會返回一個跟類A相關,而且包含了物件所有變數值的字串。 如果要想在另外一個檔案中反序列化一個物件,這個物件的類必須在反序列化之前定義,可以通過包含一個定義該類的檔案或使用函式spl_autoload_register()

來實現。

<?php
// classa.inc:
  
  class A {
      public $one = 1;
    
      public function show_one() {
	  echo $this->one;
      }
  }
  
// page1.php:

  include("classa.inc");
  
  $a = new A;
  $s = serialize($a);
  // 把變數$s儲存起來以便檔案page2.php能夠讀到
  file_put_contents('store', $s);

// page2.php:
  
  // 要正確反序列化,必須包含下面一個檔案
  include("classa.inc");

  $s = file_get_contents('store');
  $a = unserialize($s);

  // 現在可以使用物件$a裡面的函式 show_one()
  $a->show_one();
?>

可以在物件上使用 __sleep()__wakeup() 方法對序列化/反序列化事件掛載鉤子。 使用 __sleep() 也能夠讓你僅序列化物件的某些屬性

2.魔術方法

2.1 __sleep()__wakeup()

public __sleep(): array

public __wakeup(): void

serialize() 函式會檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被呼叫,然後才執行序列化操作。此功能可以用於清理物件,並返回一個包含物件中所有應被序列化的變數名稱的陣列。如果該方法未返回任何內容,則 null 被序列化,併產生一個 E_NOTICE 級別的錯誤。

注意:

__sleep() 不能返回父類的私有成員的名字。這樣做會產生一個 E_NOTICE 級別的錯誤。可以用 Serializable 介面來替代

__sleep() 方法常用於提交未提交的資料,或類似的清理操作。同時,如果有一些很大的物件,但不需要全部儲存,這個功能就很好用。

與之相反,unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先呼叫 __wakeup 方法,預先準備物件需要的資源。

__wakeup() 經常用在反序列化操作中,例如重新建立資料庫連線,或執行其它初始化操作。

示例 #1 Sleep 和 wakeup

<?php
class Connection 
{
    protected $link;
    private $server, $username, $password, $db;
    
    public function __construct($server, $username, $password, $db)
    {
	$this->server = $server;
	$this->username = $username;
	$this->password = $password;
	$this->db = $db;
	$this->connect();
    }
    
    private function connect()
    {
	$this->link = mysql_connect($this->server, $this->username, $this->password);
	mysql_select_db($this->db, $this->link);
    }
    
    public function __sleep()
    {
	return array('server', 'username', 'password', 'db');
    }
    
    public function __wakeup()
    {
	$this->connect();
    }
}
?>

2.2 __serialize()__unserialize()

注意:
此特性自 PHP 7.4.0 起可用。  

public __serialize(): array
public __unserialize(array $data): void

serialize() 函式會檢查類中是否存在一個魔術方法 __serialize()。如果存在,該方法將在任何序列化之前優先執行。它必須以一個代表物件序列化形式的 鍵/值 成對的關聯陣列形式來返回,如果沒有返回陣列,將會丟擲一個 TypeError 錯誤

注意:
如果類中同時定義了 __serialize() 和 __sleep() 兩個魔術方法,則只有 __serialize() 方法會被呼叫。 __sleep() 方法會被忽略掉。<br>如果物件實現了 Serializable 介面,介面的 serialize() 方法會被忽略,做為代替類中的 __serialize() 方法會被呼叫。
如果類中同時定義了 __unserialize() 和 __wakeup() 兩個魔術方法,則只有 __unserialize() 方法會生效,__wakeup() 方法會被忽略。

示例 #2 序列化和反序列化

<?php
class Connection
{
    protected $link;
    private $dsn, $username, $password;

    public function __construct($dsn, $username, $password)
    {
	$this->dsn = $dsn;
	$this->username = $username;
	$this->password = $password;
	$this->connect();
    }

    private function connect()
    {
	$this->link = new PDO($this->dsn, $this->username, $this->password);
    }

    public function __serialize(): array
    {
	return [
	  'dsn' => $this->dsn,
	  'user' => $this->username,
	  'pass' => $this->password,
	];
    }

    public function __unserialize(array $data): void
    {
	$this->dsn = $data['dsn'];
	$this->username = $data['user'];
	$this->password = $data['pass'];

	$this->connect();
    }
}?>

3. 序列化介面

3.1 簡介

序列化介面文件 - 詳情

自定義序列化的介面。
實現此介面的類將不再支援 __sleep()__wakeup()。不論何時,只要有例項需要被序列化,serialize 方法都將被呼叫。它將不會呼叫 __destruct() 或有其他影響,除非程式化地呼叫此方法。當資料被反序列化時,類將被感知並且呼叫合適的 unserialize() 方法而不是呼叫 __construct()。如果需要執行標準的構造器,你應該在這個方法中進行處理。

3.2 介面摘要

class Serializable {
	/* 方法 */
	abstract public serialize(): string
	abstract public unserialize(string $serialized): mixed
}

示例 #1 Basic usage

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

$newobj = unserialize($ser);

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

以上例程的輸出類似於:

string(15) "My private data"

示例 #2 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);
    }

}

示例 #3 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