PHP序列化和反序列化详解

最近,在看PHP官方文档时,发现官方文档中关于序列化的内容并不是非常详细,下面我就详细介绍一下PHP序列化和反序列化

serialize()和unserialize()函数

用法

  • serialize()函数可以把数字、字符串、数组以及对象进行序列化,并返回序列化后的字符串。
  • unserialize()函数可以把序列化的字符串你,还原原来的结构

实例

整型

序列化和反序列化整型变量

<?php
$data = 56;
$serialized_data = serialize($data);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

序列化:i:56;
反序列化:56

浮点型

序列化和反序列化浮点型变量

输入:56.012

<?php
$data = 56.012;
$serialized_data = serialize($data);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

序列化:d:56.012;
反序列化:56.012

输入:56.452

<?php
$data = 56.452;
$serialized_data = serialize($data);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

序列化:d:56.451999999999998;
反序列化:56.452

字符串

序列化和反序列化字符串变量

<?php
$data = "acdef";
$serialized_data = serialize($data);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

序列化:s:5:"acdef";
反序列化:acdef

数组

序列化和反序列化数组

<?php
$data = array("Thor","Iornman","Cap");
$serialized_data = serialize($data);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:". PHP_EOL;
var_dump($unserialized_data);
?>

输出:

反序列化:
array (size=3)
  0 => string 'Thor' (length=4)
  1 => string 'Iornman' (length=7)
  2 => string 'Cap' (length=3)
序列化:a:3:{i:0;s:4:"Thor";i:1;s:7:"Iornman";i:2;s:3:"Cap";}

对象

序列化和反序列化对象,serialize只会把对象成员变量序列化。

<?php
class Book{
    private $num;
    private $title;
    private $writer;
    function __construct($num,$title,$writer){
        $this->num = $num;
        $this->title = $title;
        $this->writer = $writer;
    }

    private function test(){
        echo "test";
    }
}

$book = new Book(001,"算法","作者");

$serialized_data = serialize($book);
echo  "序列化:".$serialized_data . PHP_EOL;

$unserialized_data = unserialize($serialized_data);
echo  "反序列化:". PHP_EOL;
var_dump($unserialized_data);
?>

输出:

反序列化:
object(Book)[2]
  private 'num' => int 1
  private 'title' => string '算法' (length=6)
  private 'writer' => string '作者' (length=6)
序列化:O:4:"Book":3:{s:9:"Booknum";i:1;s:11:"Booktitle";s:6:"算法";s:12:"Bookwriter";s:6:"作者";}

另:序列化对象时,不会保存常量的值。对于父类中的变量,则会保留。

当反序列化对象之前,未定义类时会正常输入,但反序列化得到的对象是__PHP_Incomplete_Class,并指定了未定义类的类名。

$serialized_data = 'O:4:"User":2:{s:8:"username";s:7:"Iornman";s:8:"nickname";s:6:"尼尼";}';
var_dump(unserialize($serialized_data));

输出:

object(__PHP_Incomplete_Class)[1]
  public '__PHP_Incomplete_Class_Name' => string 'User' (length=4)
  public 'username' => string 'Iornman' (length=7)
  public 'nickname' => string '尼尼' (length=6)

其他

布尔类型和NUll序列化后的字符串格式如下:

  • Boolean : b:value;(1或0)
  • Null : N;

sleep()和wakeup()魔术方法

有时,我们并不想将对象中的所有成员变量都进行序列化,而是有选择的进行序列化,这时就要用到__sleep()魔术方法。

__sleep()用法

当时使用serialize()进行序列化对象时,如果存在__sleep()魔术方法,则会先调用此方法,然后才执行序列化。

__wakeup()用法

当时使用unserialize()进行反序列化对象时,如果存在__wakeup()魔术方法,则会先调用此方法,然后才执行反序列化。

实例

利用__sleep()魔术方法过滤掉一些敏感变量,使其不进行序列化。

<?php
class User{
    public $username;
    public $nickname;
    private $password;

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

    // 重载序列化调用的方法
    public function __sleep()
    {
        // 返回需要序列化的变量名,过滤掉password变量
        return array('username', 'nickname');
    }

    public function __wakeup(){
        echo "执行__wakeup";
    }
}
$user = new User('Iornman', '尼尼', '123456');

$serialized_data = serialize($user);
var_dump($serialized_data);

$unserialized_data = unserialize($serialized_data);
var_dump($unserialized_data);
?>

输出:

string 'O:4:"User":2:{s:8:"username";s:7:"Iornman";s:8:"nickname";s:6:"尼尼";}' (length=72)
执行__wakeup
object(User)[2]
  public 'username' => string 'Iornman' (length=7)
  public 'nickname' => string '尼尼' (length=6)
  private 'password' => null

序列化接口Serializable

当使用__sleep()时,存在一个问题,__sleep()无法将父类的成员变量序列化,我们可以使用Serializable序列化接口来解决这个问题。

实例

让父类继承Serializable接口,并在父类中重写Serializable提供的serialize()unserialize()方法。当父类继承Serializable接口时,子类中的__sleep()__wakeup()将不再被调用。

<?php
class Common implements Serializable{
    public $publc_val;
    private $private_val = '123';

    //重写Serializable接口提供的方法
    public function serialize()
    {
        echo __METHOD__ . "\n";
        return serialize($this->private_val);
    }
    //重写Serializable接口提供的方法
    public function unserialize($serialized)
    {
        echo __METHOD__ . "\n";
    }
}

class User extends Common{
    public $username;
    public $nickname;
    private $password;

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

    // 重载序列化调用的方法
    public function __sleep()
    {
        // 返回需要序列化的变量名,过滤掉password变量
        echo __METHOD__;
    }

    public function __wakeup(){
        echo __METHOD__;
    }
}

$user = new User('Iornman', '尼尼', '123456');

$serialized_data = serialize($user);
var_dump($serialized_data);

$unserialized_data = unserialize($serialized_data);
var_dump($unserialized_data);
?>

输出:

Common::serialize
object(User)[2]
  public 'username' => null
  public 'nickname' => null
  private 'password' => null
  public 'publc_val' => null
  private 'private_val' (Common) => null
Common::unserialize
string 'C:4:"User":10:{s:3:"123";}' (length=26)