您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何使用Serializable接口來自定義PHP中類的序列化”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何使用Serializable接口來自定義PHP中類的序列化”吧!
關于PHP中的對象序列化這件事兒,之前我們在很早前的文章中已經提到過 __sleep() 和 __weakup() 這兩個魔術方法。今天我們介紹的則是另外一個可以控制序列化內容的方式,那就是使用 Serializable 接口。它的使用和上述兩個魔術方法很類似,但又稍有不同。
class A implements Serializable {
private $data;
public function __construct(){
echo '__construct', PHP_EOL;
$this->data = "This is Class A";
}
public function serialize(){
echo 'serialize', PHP_EOL;
return serialize($this->data);
}
public function unserialize($data){
echo 'unserialize', PHP_EOL;
$this->data = unserialize($data);
}
public function __destruct(){
echo '__destruct', PHP_EOL;
}
public function __weakup(){
echo '__weakup', PHP_EOL;
}
public function __sleep(){
echo '__destruct', PHP_EOL;
}
}
$a = new A();
$aSerialize = serialize($a);
var_dump($aSerialize);
// "C:1:"A":23:{s:15:"This is Class A";}"
$a1 = unserialize($aSerialize);
var_dump($a1);
這段代碼就是使用 Serializable 接口來進行序列化處理的,注意一點哦,實現了 Serializable 接口的類中的 __sleep() 和 __weakup() 魔術方法就無效了哦,序列化的時候不會進入它們。
Serializable 這個接口需要實現的是兩個方法,serialize() 方法和 unserialize() 方法,是不是和那兩個魔術方法完全一樣。當然,使用的方式也是一樣的。
在這里,我們多普及一點序列化的知識。對象序列化只能序列化它們的屬性,不能序列化他們方法。如果當前能夠找到對應的類模板,那么可以還原出這個類的方法來,如果沒有定義過這個類的模板,那么還原出來的類是沒有方法只有屬性的。我們通過這段代碼中的序列化字符串來分析:
下面我們再來看下不同類型序列化的結果。要知道,在PHP中,我們除了句柄類型的數據外,其他標量類型或者是數組、對象都是可以序列化的,它們在序列化字符串中是如何表示的呢?
$int = 110;
$string = '110';
$bool = FALSE;
$null = NULL;
$array = [1,2,3];
var_dump(serialize($int)); // "i:110;"
var_dump(serialize($string)); // "s:3:"110";"
var_dump(serialize($bool)); // "b:0;"
var_dump(serialize($null)); // "N;"
var_dump(serialize($array)); // "a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}"
上面的內容還是比較好理解的吧。不過我們還是一一說明一下:
接下來,我們重點講講對象類型,上面已經提到過,實現 Serializable 接口的對象序列化后的標識是有特殊情況的。上方序列化后的字符串開頭類型標識為 "C:",那么我們看看不實現 Serializable 接口的對象序列化后是什么情況。
// 正常對象類型序列化的結果
class B {
private $data = "This is Class B";
}
$b = new B();
$bSerialize = serialize($b);
var_dump ($bSerialize); // "O:1:"B":1:{s:7:"Bdata";s:15:"This is Class B";}"
var_dump($bSerialize);
var_dump(unserialize("O:1:\"B\":1:{s:7:\"\0B\0data\";s:15:\"This is Class B\";}"));
// object(B)#4 (1) {
// ["data":"B":private]=>string(15) "This is Class B"
// }
果然,它開頭的類型標識是 "O:"。那么我們可以看出,"C:" 很大的概率指的是當前序列化的內容是一個類類型,不是一個對象類型。它們之間其實并沒有顯著的差異,包括官方文檔上也沒有找到特別具體的說明。如果有過這方面的研究或者有相關資料的同學可以評論留言一起討論哈。
此外,如果我們手動將一個對象的 "O:" 轉成 "C:" 會怎么樣呢?
// 把O:替換成C:
var_dump(unserialize(str_replace('O:', 'C:', $bSerialize))); // false
抱歉,無法還原了。那么我們反過來,將上面 A 類也就是實現了 Serializable 接口的序列化字符串中的 "C:" 轉成 "O:" 呢?
// Warning: Erroneous data format for unserializing 'A'
var_dump(unserialize(str_replace('C:', 'O:', $aSerialize))); // false
嗯,會提示一個警告,然后同樣也無法還原了。這樣看來,我們的反序列化還是非常智能的,有一點點的不同都無法進行還原操作。
最后,我們來看看未定義類的情況下,直接反序列化一個對象。
// 模擬一個未定義的D類
var_dump(unserialize("O:1:\"D\":2:{s:7:\"\0D\0data\";s:15:\"This is Class D\";s:3:\"int\";i:220;}"));
// object(__PHP_Incomplete_Class)#4 (3) {
// ["__PHP_Incomplete_Class_Name"]=>string(1) "D"
// ["data":"D":private]=>string(15) "This is Class D"
// ["int"]=>int(220)
// }
// 把未定義類的O:替換成C:
var_dump(unserialize(str_replace('O:', 'C:', "O:1:\"D\":2:{s:7:\"\0D\0data\";s:15:\"This is Class D\";s:3:\"int\";i:220;}"))); // false
從代碼中,我們可以看出,"C:" 類型的字符串依然無法反序列化成功。劃重點哦,如果是C:開頭的序列化字符串,一定需要是定義過的且實現了 Serializable 接口的類 才能反序列化成功。
另外,我們可以發現,當序列化字符串中的模板不存在時,反序列化出來的類的類名是 __PHP_Incomplete_Class_Name 類,不像有類模板的反序列化成功直接就是正常的類名。
其實從以上各種來看,個人感覺如果要保存數據或者傳遞數據的話,序列化并不是最好的選擇。畢竟包含了類型以及長度后將使得格式更為嚴格,而且反序列化回來的內容如果沒有對應的類模板定義也并不是特別好用的,還不如直接使用 JSON 來得方便易讀。當然,具體情況具體分析,我們還是要結合場景來選擇合適的使用方式。
測試代碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202003/source/%E4%BD%BF%E7%94%A8Serializable%E6%8E%A5%E5%8F%A3%E6%9D%A5%E8%87%AA%E5%AE%9A%E4%B9%89PHP%E4%B8%AD%E7%B1%BB%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96.php
參考文檔:
https://www.php.net/manual/zh/class.serializable.php
感謝各位的閱讀,以上就是“如何使用Serializable接口來自定義PHP中類的序列化”的內容了,經過本文的學習后,相信大家對如何使用Serializable接口來自定義PHP中類的序列化這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。