在php中,序列化用与存储或传递php的值的过程,同时不丢失其结构和数据类型。函数包括serialize()、unserialize();魔术方法包括__sleep() wakeup(); 预定义接口Serializable;
目录
【1】serialize()与unserialize()
serialize() 返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。这有利于存储或传递 PHP 的值,同时不丢失其类型和结构。serialize() 可处理除了 resource 之外的任何类型。甚至可以 serialize() 那些包含了指向其自身引用的数组。你正 serialize() 的数组/对象中的引用也将被存储。
函数用法:
serialize ( mixed $value ) : string
作用:
用于序列化对象或数组,并返回一个字符串。
例子:
$sites = ['Google','bing','Baidu'];
$data = serialize($sites);
echo $data;//a:3:{i:0;s:6:"Google";i:1;s:4:"bing";i:2;s:5:"Baidu";}
unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
函数用法:
unserialize ( string $str ) : mixed
作用:
对单一的已序列化的变量进行操作,将其转换回 PHP 的值
例子:
$str = 'a:3:{i:0;s:6:"Google";i:1;s:4:"bing";i:2;s:5:"Baidu";}';
$data=unserialize($str);
var_dump($data);
/*
array (size=3)
0 => string 'Google' (length=6)
1 => string 'bing' (length=4)
2 => string 'Baidu' (length=5)
*/
【2】各种变量的序列化结果
以下表格也是我实践看到的,可能不是很官方或者全面。
变量类型 | 进行serialize | 标志 |
---|---|---|
int | i:9; | i |
string | s:5:"Tacks"; | s |
double | d:9.9000000000000004; | d |
bool | b:1; | b |
array | a:4:{i:0;i:1;i:1;s:3:"two";i:2;d:2;i:3;b:1;} | a |
null | N; | N |
object | O:6:"People":0:{} | O |
$num = 9;
$data = serialize($num);
echo $data,'<br/>';//i:9;
$str = 'Tacks';
$data = serialize($str);
echo $data,'<br/>';//s:5:"Tacks";
$decimal = 9.9;
$data = serialize($decimal);
echo $data,'<br/>';//d:9.9000000000000004;
$bool = true;
$data = serialize($bool);
echo $data,'<br/>';//b:1;
$arr = [1,'two',2.0,true];
$data = serialize($arr);
echo $data,'<br/>';//a:4:{i:0;i:1;i:1;s:3:"two";i:2;d:2;i:3;b:1;}
$null = null;
$data = serialize($null);
echo $data,'<br/>';//N;
class People{};
$obj = new People;
$data = serialize($obj);
echo $data,'<br/>';//O:6:"People":0:{}
$res = opendir('../written');//打开一个目录返回资源类型
$data = serialize($res);
echo $data,'<br/>';//i:0; #实际上最好不去执行资源类型
function my_callback_function() {
echo 'GGG';
}
$back = call_user_func('my_callback_function');
$data = serialize($back);
echo $data,'<br/>';//GGGN; #这个回调
【3】序列化对象
序列化对象时,不会保存常量的值;对象的受保护类型的属性名,会有一个*号区别,对于父类中的变量,则会保留;可以看一下下面的例子。
class Animal{
public $type = 'Animal';
}
class Bird extends Animal{
const wings = 2;
public $name;
protected $age;
private $weight;
public function __construct($name,$age,$weight){
$this->name = $name;
$this->age = $age;
$this->weight = $weight;
}
}
$bigBird = new Bird('Big sparrow',9,'10KG');
echo serialize($bigBird);
/*
输出的结果整理一下格式看的清楚一些
O:4:"Bird":4:{
s:4:"name";s:11:"Big sparrow";
s:6:"*age";i:9;
s:12:"Birdweight";s:4:"10KG";
s:4:"type";s:6:"Animal";
}
可以看出,
*/
【4】序列化对象中的__sleep()魔术方法
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
魔术方法:
public array __sleep ( void );
作用:
对象被序列化时候,先调用此方法,然后执行序列化。
返回值:
1.返回一个包含对象中所有应被序列化的变量名称的数组。
2.如果该方法未返回任何内容,则 NULL 被序列化,报 E_NOTICE 级别的错误。
class Bird {
const wings = 2;
public $name;
protected $age;
private $weight;
public function __construct($name,$age,$weight){
$this->name = $name;
$this->age = $age;
$this->weight = $weight;
}
//使用__sleep()魔术方法 自定义序列化行为
public function __sleep(){
return ['weight'];#返回对象的私有属性
}
}
$bigBird = new Bird('Big sparrow',9,'10KG');
#执行序列化,经过__sleep(),会返回特定的值
echo serialize($bigBird);//O:4:"Bird":1:{s:12:"Birdweight";s:4:"10KG";}
【5】file_put_contents保存序列化值到文件
函数用法:
file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) : int
作用:
将一个字符串写入文件
参数:
$filename 被写入数据的文件名,不存在会自动创建,默认存在的话内容被覆盖。
$data 要写入的数据。类型可以是 string,array 或者是 stream 资源
$flags
FILE_USE_INCLUDE_PATH 在 include 目录里搜索 filename
FILE_APPEND 如果文件 filename 已经存在,追加数据而不是覆盖。
LOCK_EX 在写入时获得一个独占锁。
返回值:
该函数将返回写入到文件内数据的字节数,失败时返回FALSE
#将上面的代码序列化的值存入文件中
$bigBird = new Bird('Big sparrow',9,'10KG');
$ser = serialize($bigBird);
file_put_contents('Bird.ser', $ser,FILE_APPEND);
//O:4:"Bird":1:{s:12:"Birdweight";s:4:"10KG";}
【6】反序列化对象中的__wakeup()魔术方法
unserialize()反序列化函数用于将单一的已序列化的变量转换回 PHP 的值。如果是反序列化后是对象,那么会调用__wakeup()魔术方法。__wakeup()经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
魔术方法:
public __sleep ( void ) : array
作用:
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
返回值:
如果传递的字符串是正确的序列化值,会返回对应的变量值。
如果传递的字符串不可解序列化,则返回 FALSE,并产生一个E_NOTICE。
class Bird {
const wings = 2;
public $name;
protected $age;
private $weight;
public function __construct($name,$age,$weight){
$this->name = $name;
$this->age = $age;
$this->weight = $weight;
}
//使用__wakeup()魔术方法 定义反序列化后执行调用的方法
public function __wakeup(){
echo 'WakeUp WakeUp WakeUp';
}
}
$bigBird = new Bird('Big sparrow',9,'10KG');
$ser = serialize($bigBird);
$obj = unserialize($ser);//反序列化后成功构造一个新对象,然后调用__wakeup方法执行WakeUp WakeUp WakeUp
var_dump($obj);//打印对象
echo $obj->name;//输出对象属性
/*
WakeUp WakeUp WakeUp
object(Bird)[2]
public 'name' => string 'Big sparrow' (length=11)
protected 'age' => int 9
private 'weight' => string '10KG' (length=4)
Big sparrow
*/
【7】序列化接口Serializable
这个Serializable,是PHP中预定义的。实现此接口的类将不再支持 sleep() 和 wakeup()。不论何时,只要有实例需要被序列化,serialize 方法都将被调用。当数据被反序列化时,类将被感知并且调用合适的 unserialize() 方法而不是调用构造函数__ construct()。如果需要执行标准的构造器,你应该在这个方法中进行处理。
Serializable {
abstract public serialize ( void ) : string
abstract public unserialize ( string $serialized ) : mixed
}
写一个类实现Serializable接口,然后实现serialize方法和unserialize方法。
class obj implements Serializable {
private $data;
public function __construct() {
$this->data = "My private data";
}
#实现接口的方法serialize
public function serialize() {
return serialize($this->data);
}
#实现接口的方法unserialize
public function unserialize($data) {
$this->data = unserialize($data);
}
public function getData() {
return $this->data;
}
public function __sleep(){#实现Serializable的接口 __sleep不会被调用
echo '__sleep()';
}
public function __wakup(){#实现Serializable的接口 __wakup不会被调用
echo '__wakup()';
}
}
$obj = new obj;
$ser = serialize($obj);
$newobj = unserialize($ser);
var_dump($newobj->getData());//string 'My private data' (length=15)
【8】小结
序列化和反序列化是一种传输抽象数据的思想。通过定义序列化和反序列化的规则,我们可以实现将PHP中的对象序列化成字节流。在PHP中很序列化用的不多,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
另外PHP还有另外一种常见方式json去把数组或者对象转化成字符串,也就是用json_encode()和json_decode()函数,不仅转化后,数据很直观,而且这种方法更迅速,听说大概快两三倍。