詳解PHP序列化和反序列化原理
0.前言
對(duì)象的序列化和反序列化作用就不再贅述,php中序列化的結(jié)果是一個(gè)php自定義的字符串格式,有點(diǎn)類(lèi)似json.
我們?cè)谌魏握Z(yǔ)言中設(shè)計(jì)對(duì)象的序列化和反序列化都需要解決幾個(gè)問(wèn)題
把某個(gè)對(duì)象序列化之后,序列化的結(jié)果有自描述的功能(從序列化的結(jié)果中知道這個(gè)對(duì)象的具體類(lèi)型,
知道類(lèi)型還不夠,當(dāng)然還需要知道這個(gè)類(lèi)型所對(duì)應(yīng)具體的值).
序列化時(shí)的權(quán)限控制,可以自定義序列化字段等,例如golang中的做的就非常方便.
時(shí)間性能問(wèn)題:在某些性能敏感的場(chǎng)景下,對(duì)象序列化就不能拖后腿,例如:高性能服務(wù)(我經(jīng)常使用protobuf來(lái)序列化).
空間性能問(wèn)題:序列化之后的結(jié)果不能太長(zhǎng),比如內(nèi)存中一個(gè)int對(duì)象,序列化之后數(shù)據(jù)長(zhǎng)度變成了10倍int的長(zhǎng)度,那這個(gè)序列化算法是有問(wèn)題的.
本文僅僅從php代碼角度來(lái)解釋php中序列化和反序列化的過(guò)程.,記住一點(diǎn)序列化和反序列化操作的僅僅是對(duì)象的數(shù)據(jù),這一點(diǎn)有面向?qū)ο箝_(kāi)發(fā)經(jīng)驗(yàn)的都應(yīng)該容易理解.
1.序列化serialize和反序列化方法unserialize
php原生提供了對(duì)象序列化功能,不像c++ ……^_^. 用起來(lái)也非常簡(jiǎn)單,就兩個(gè)接口.
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); //通過(guò)serialize接口序列化
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);//通過(guò)unserialize反序列化
$toobj->print();
fobnn
O:5:"fobnn":2:{s:7:"hack_id";i:1;s:16:"fobnnhack_name";s:5:"fobnn";}
fobnn
看到第二行的輸出,這個(gè)字符串就是序列化的結(jié)果,這個(gè)結(jié)構(gòu)其實(shí)很容讀懂,可以發(fā)現(xiàn)是通過(guò)對(duì)象名稱(chēng)/成員名稱(chēng)來(lái)映射的,當(dāng)然不同訪(fǎng)問(wèn)權(quán)限的成員序列化之后的標(biāo)簽名稱(chēng)略有不同.
根據(jù)我上面講到的3個(gè)問(wèn)題,那么我們可以來(lái)看看
1.自描述功能
O:5:"fobnn":2 其中o就表示了object類(lèi)型,且類(lèi)型名稱(chēng)為fobnn, 采用這種格式,后面的2表示了有2個(gè)成員對(duì)象.
關(guān)于成員對(duì)象,其實(shí)也是同一套子描述,這是一個(gè)遞歸的定義.
自描述的功能主要是通過(guò)字符串記錄對(duì)象和成員的名稱(chēng)來(lái)實(shí)現(xiàn).
2.性能問(wèn)題
php序列化的時(shí)間性能本文就不分析了,詳見(jiàn)后面,但序列化結(jié)果其實(shí)類(lèi)似json/bson定義的協(xié)議,有協(xié)議頭,協(xié)議頭說(shuō)明了類(lèi)型,協(xié)議體則說(shuō)明了類(lèi)型所對(duì)應(yīng)的值,并不會(huì)對(duì)序列化結(jié)果進(jìn)行壓縮.
2.反序列化中的魔術(shù)方法
對(duì)應(yīng)上述說(shuō)的第二個(gè)問(wèn)題,其實(shí)php中也有解決方法,一種是通過(guò)魔術(shù)方法,第二種則是自定義序列化函數(shù).先來(lái)介紹下魔術(shù)方法 __sleep和__wakeup
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;
}
public function __sleep()
{
return array("hack_name");
}
public function __wakeup()
{
$this->hack_name = 'haha';
}
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
fobnn
O:5:"fobnn":1:{s:16:"fobnnhack_name";s:5:"fobnn";}
haha
在序列化之前會(huì)先調(diào)用__sleep返回的是一個(gè)需要序列化的成員名稱(chēng)數(shù)組,通過(guò)這樣我們就可以控制需要序列化的數(shù)據(jù),案例中我只返回了hack_name,可以看到結(jié)果中只序列化了hack_name成員.
在序列化完成之后,會(huì)跳用__wakeup 在這里我們可以做一些后續(xù)工作,例如重連數(shù)據(jù)庫(kù)之類(lèi)的.
3.自定義Serializable接口
interface Serializable {
abstract public string serialize ( void )
abstract public void unserialize ( string $serialized )
}
通過(guò)這個(gè)接口我們可以自定義序列化和反序列化的行為,這個(gè)功能主要可以用來(lái)自定義我們的序列化格式.
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('hack_name');
}
public function __wakeup()
{
$this->hack_name = 'haha';
}
public function serialize()
{
return json_encode(array('id' => $this->hack_id ,'name'=>$this->hack_name ));
}
public function unserialize($var)
{
$array = json_decode($var,true);
$this->hack_name = $array['name'];
$this->hack_id = $array['id'];
}
}
$obj = new fobnn('fobnn',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
當(dāng)使用了自定義序列化接口之后,我們的魔術(shù)方法就沒(méi)用了.
4.PHP動(dòng)態(tài)類(lèi)型和PHP反序列化
既然上文中提到的自描述功能,那么序列化結(jié)果中保存了對(duì)象的類(lèi)型,且php是動(dòng)態(tài)類(lèi)型語(yǔ)言,那么我們就可以來(lái)做個(gè)簡(jiǎn)單的實(shí)驗(yàn).
class fobnn
{
public $hack_id;
public $hack_name;
public function __construct($name,$id)
{
$this->hack_name = $name;
$this->hack_id = $id;
}
public function print()
{
var_dump($this->hack_name);
}
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
$toobj2 = unserialize("O:5:\"fobnn\":2:{s:7:\"hack_id\";i:1;s:9:\"hack_name\";i:12345;}");
$toobj2->print();
我們修改hack_name反序列化的結(jié)果為int類(lèi)型, 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)
可以發(fā)現(xiàn),對(duì)象成功序列化回來(lái)了!并且可以正常工作!. 當(dāng)然php的這種機(jī)制提供了靈活多變的語(yǔ)法,但也引入了安全風(fēng)險(xiǎn). 后續(xù)繼續(xù)分析php序列化和反序列化特性帶來(lái)的安全問(wèn)題.
以上就是我們整理的關(guān)于PHP序列化和反序列化原理的全部知識(shí)內(nèi)容,感謝你對(duì)腳本之家的支持。
相關(guān)文章
linux php mysql數(shù)據(jù)庫(kù)備份實(shí)現(xiàn)代碼
想在PHP后臺(tái)管理直接能夠備份數(shù)據(jù)庫(kù),于是想呀想,一直沒(méi)有什么思路,一開(kāi)始是考慮用php來(lái)訪(fǎng)問(wèn)服務(wù)器安裝mysql的目錄,比如 /usr/local/mysql/data目錄,直接把下面對(duì)應(yīng)的文件進(jìn)行備份2009-03-03
PHP正則表達(dá)式 /i, /is, /s, /isU等介紹
PHP正則表達(dá)式 /i, /is, /s, /isU等,都代表著什么意思,你知道嗎?下面為大家詳細(xì)介紹下2014-10-10
thinkphp5 migrate數(shù)據(jù)庫(kù)遷移工具
這里講述的是tp5 migrate數(shù)據(jù)庫(kù)遷移工具的相關(guān)介紹,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以來(lái)看下本文的實(shí)例2018-02-02
針對(duì)PHP開(kāi)發(fā)安全問(wèn)題的相關(guān)總結(jié)
今天小編就為大家分享一篇關(guān)于針對(duì)PHP開(kāi)發(fā)安全問(wèn)題的相關(guān)總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
如何在symfony中導(dǎo)出為CSV文件中的數(shù)據(jù)
如果您需要在symfony中將數(shù)據(jù)庫(kù)中的數(shù)據(jù)導(dǎo)出為CSV文件,試試這個(gè)2011-10-10
用php或asp創(chuàng)建網(wǎng)頁(yè)桌面快捷方式的代碼
上傳到網(wǎng)站,shortcut.php 就會(huì)有提示下載一個(gè)名為 張楚網(wǎng)站.urll文件,保存在本地就是一個(gè)快捷方式!2010-03-03
PHP實(shí)現(xiàn)帶重試功能的curl連接示例
這篇文章主要介紹了PHP實(shí)現(xiàn)帶重試功能的curl連接方法,結(jié)合實(shí)例形式分析了php使用curl實(shí)現(xiàn)重復(fù)連接的方法,需要的朋友可以參考下2016-07-07

