首頁  >  文章  >  後端開發  >  直擊PHP序列化與反序列化原理

直擊PHP序列化與反序列化原理

coldplay.xixi
coldplay.xixi轉載
2020-07-20 17:23:002204瀏覽

直擊PHP序列化與反序列化原理

0.前言

物件的序列化和反序列化作用就不再贅述,php中序列化的結果是一個php自訂的字串格式,有點類似json.

我們在任何語言中設計物件的序列化和反序列化都需要解決幾個問題

#把某個物件序列化之後,序列化的結果有自描述的功能(從序列化的結果中知道這個物件的具體類型,

##知道類型還不夠,當然還需要知道這個類型所對應具體的值).


序列化時的權限控制,可以自訂序列化欄位等,例如golang中的做的就非常方便.


時間性能問題:在某些性能敏感的場景下,對象序列化就不能拖後腿,例如:高性能服務(我經常使用protobuf來序列化).


空間性能問題:序列化之後的結果不能太長,比如記憶體中一個int物件,序列化之後資料長度變成了10倍int的長度,那這個序列化演算法是有問題的.


本文僅僅從php程式碼角度來解釋php中序列化和反序列化的過程.,記住一點序列化和反序列化操作的僅僅是對象的數據,這一點有面向對象開發經驗的都應該容易理解.

相關學習推薦:

PHP程式設計從入門到精通

#1.序列化serialize與反序列化方法unserialize
##1.序列化serialize與反序列化方法unserialize

#php原生提供了物件序列化功能,不像c …^_^. 用起來也非常簡單,就兩個介面.

class fobnn
{
 public $hack_id;
 private $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  echo $this->hack_name.PHP_EOL;
 }
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj); //通过serialize接口序列化
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);//通过unserialize反序列化
$toobj->print();
fobnn
O:5:"fobnn":2:{s:7:"hack_id";i:1;s:16:"fobnnhack_name";s:5:"fobnn";}
fobnn

看到第二行的輸出,這個字串就是序列化的結果,這個結構其實很容讀懂,可以發現是透過物件名稱/成員名稱來映射的,當然不同存取權限的成員序列化之後的標籤名稱略有不同.

根據我上面講到的3個問題,那麼我們可以來看看

1.自描述功能

O:5:"fobnn":2 其中o就表示了object類型,且型別名稱為fobnn, 採用這種格式,後面的2表示了有2個成員對象.

關於成員對象,其實也是同一套子描述,這是一個遞歸的定義.

自描述的功能主要是透過字串記錄物件和成員的名稱來實現.

2.效能問題

php序列化的時間效能本文就不分析了,詳見後面,但序列化結果其實類似json/bson定義的協議,有協議頭,協議頭說明了類型,協議體則說明了類型所對應的值,並不會對序列化結果進行壓縮.
2.反序列化中的魔術方法

對應上述說的第二個問題,其實php中也有解決方法,一種是透過魔術方法,第二種則是自訂序列化函數.先來介紹下魔術方法__sleep和__wakeup<pre class="brush:html;toolbar:false;">class fobnn { public $hack_id; private $hack_name; public function __construct($name,$id) { $this-&gt;hack_name = $name; $this-&gt;hack_id = $id; } public function print() { echo $this-&gt;hack_name.PHP_EOL; } public function __sleep() { return array(&quot;hack_name&quot;); } public function __wakeup() { $this-&gt;hack_name = &amp;#39;haha&amp;#39;; } } $obj = new fobnn(&amp;#39;fobnn&amp;#39;,1); $obj-&gt;print(); $serializedstr = serialize($obj); echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr); $toobj-&gt;print();</pre><pre class="brush:html;toolbar:false;">fobnn O:5:&quot;fobnn&quot;:1:{s:16:&quot;fobnnhack_name&quot;;s:5:&quot;fobnn&quot;;} haha</pre>在序列化之前會先調用__sleep返回的是一個需要序列化的成員名稱數組,透過這樣我們就可以控制需要序列化的資料,案例中我只回傳了

hack_name

,可以看到結果中只序列化了hack_name成員.在序列化完成之後,會跳用

__wakeup

在這裡我們可以做一些後續工作,例如重連資料庫之類的.

3.自訂Serializable介面

interface Serializable {
abstract public string serialize ( void )
abstract public void unserialize ( string $serialized )
}

透過這個介面我們可以自訂序列化和反序列化的行為,這個功能主要可以用來自訂我們的序列化格式.

class fobnn implements Serializable
{
 public $hack_id;
 private $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  echo $this->hack_name.PHP_EOL;
 }

 public function __sleep()
 {
  return array(&#39;hack_name&#39;);
 }

 public function __wakeup()
 {
  $this->hack_name = &#39;haha&#39;;
 }

 public function serialize()
 {
  return json_encode(array(&#39;id&#39; => $this->hack_id ,&#39;name&#39;=>$this->hack_name ));
 }

 public function unserialize($var)
 {
  $array = json_decode($var,true);
  $this->hack_name = $array[&#39;name&#39;];
  $this->hack_id = $array[&#39;id&#39;];
 }
}
$obj = new fobnn(&#39;fobnn&#39;,1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
fobnn
C:5:"fobnn":23:{{"id":1,"name":"fobnn"}}
fobnn

當使用了自訂序列化介面之後,我們的魔術方法就沒用了.
4.PHP動態型別和PHP反序列化

既然上文中提到的自描述功能,那麼序列化結果中保存了物件的類型,且php是動態類型語言,那麼我們就可以來做個簡單的實驗.<pre class="brush:html;toolbar:false;">class fobnn { public $hack_id; public $hack_name; public function __construct($name,$id) { $this-&gt;hack_name = $name; $this-&gt;hack_id = $id; } public function print() { var_dump($this-&gt;hack_name); } } $obj = new fobnn(&amp;#39;fobnn&amp;#39;,1); $obj-&gt;print(); $serializedstr = serialize($obj); echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr); $toobj-&gt;print(); $toobj2 = unserialize(&quot;O:5:\&quot;fobnn\&quot;:2:{s:7:\&quot;hack_id\&quot;;i:1;s:9:\&quot;hack_name\&quot;;i:12345;}&quot;); $toobj2-&gt;print();</pre>我們修改hack_name反序列化的結果為int型別,

i:12345

string(5) "fobnn"
O:5:"fobnn":2:{s:7:"hack_id";i:1;s:9:"hack_name";s:5:"fobnn";}
string(5) "fobnn"
int(12345)
###可以發現,物件成功序列化回來了!並且可以正常工作!. 當然php的這個機制提供了靈活多變的語法,但也引入了安全風險. 後續繼續分析php序列化和反序列化特性帶來的安全問題.###

以上是直擊PHP序列化與反序列化原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:jb51.net。如有侵權,請聯絡admin@php.cn刪除