我已经使用PHP几年了,但到目前为止,从未需要明确地处理序列化,只使用$_SESSION
。现在我有一个项目要求我手动实现某些数据的序列化机制 - 我发现这个问题也适用于$_SESSION
。
我有一个包含许多属性的类。大多数这些属性都很小(如内存消耗):数字,相对较短的字符串等。但是该类还包含一些属性,这些属性可能包含HUGE数组(例如,数据库表的整个转储:100,000行,每行100个字段) )。碰巧,这是需要序列化/反序列化的类之一 - 幸运的是,包含大型数组的属性不需要序列化,因为它们本质上是临时工作,并且在必要时仍然可以重建。
在Java中的这种情况下,我只是将属性声明为transient
- 并且它将从serialisaion中省略。不幸的是,PHP不支持这样的限定符。
处理的一种方法是拥有这样的东西:
class A implements Serializable
{
private $var_small = 1234;
private $var_big = array( ... ); //huge array, of course, not init in this way
public function serialize()
{
$vars = get_object_vars($this);
unset($vars['var_big']);
return serialize($vars);
}
public function unserialize($data)
{
$vars = unserialize($data);
foreach ($vars as $var => $value) {
$this->$var = $value;
}
}
}
然而,这是相当麻烦的,因为每次添加另一个瞬态属性时我都需要更新serialize
方法。此外,一旦继承发挥作用,这变得更加复杂 - 处理,因为瞬态属性可能在子类和父类中。我知道,它仍然可行,但我更愿意尽可能多地委托语言,而不是重新发明轮子。
那么,处理瞬态属性的最佳方法是什么?或者我错过了什么,PHP支持开箱即用?
答案 0 :(得分:7)
Php提供__sleep magic method,允许您选择要序列化的属性。
编辑我已经测试了__sleep()
在游戏中继承时的工作原理:
<?php
class A {
private $a = 'String a';
private $b = 'String b';
public function __sleep() {
echo "Sleep A\n";
return array( 'a');
}
}
class B extends A {
private $c = 'String c';
private $d = 'String d';
public function __sleep() {
echo "Sleep B\n";
return array( 'c');
}
}
class C extends A {
private $e = 'String e';
private $f = 'String f';
public function __sleep() {
echo "Sleep C\n";
return array_merge( parent::__sleep(), array( 'e'));
}
}
$a = new A();
$b = new B();
$c = new C();
echo serialize( $a) ."\n"; // Result: O:1:"A":1:{s:4:"Aa";s:8:"String a";}
// called "Sleep A" (correct)
echo serialize( $b) ."\n"; // Result: O:1:"B":1:{s:4:"Bc";s:8:"String c";}
// called just "Sleep B" (incorrect)
echo serialize( $c) ."\n"; // Caused: PHP Notice: serialize(): "a" returned as member variable from __sleep() but does not exist ...
// When you declare `private $a` as `protected $a` that class C returns:
// O:1:"C":2:{s:4:"*a";s:8:"String a";s:4:"Ce";s:8:"String e";}
// which is correct and called are both: "Sleep C" and "Sleep A"
因此,只有在声明为受保护的父数据时,您才可以序列化父数据: - /
编辑2 我已使用Serializable
界面尝试使用以下代码:
<?php
class A implements Serializable {
private $a = '';
private $b = '';
// Just initialize strings outside default values
public function __construct(){
$this->a = 'String a';
$this->b = 'String b';
}
public function serialize() {
return serialize( array( 'a' => $this->a));
}
public function unserialize( $data){
$array = unserialize( $data);
$this->a = $array['a'];
}
}
class B extends A {
private $c = '';
private $d = '';
// Just initialize strings outside default values
public function __construct(){
$this->c = 'String c';
$this->d = 'String d';
parent::__construct();
}
public function serialize() {
return serialize( array( 'c' => $this->c, '__parent' => parent::serialize()));
}
public function unserialize( $data){
$array = unserialize( $data);
$this->c = $array['c'];
parent::unserialize( $array['__parent']);
}
}
$a = new A();
$b = new B();
echo serialize( $a) ."\n";
echo serialize( $b) ."\n";
$a = unserialize( serialize( $a)); // C:1:"A":29:{a:1:{s:1:"a";s:8:"String a";}}
$b = unserialize( serialize( $b)); // C:1:"B":81:{a:2:{s:1:"c";s:8:"String c";s:8:"__parent";s:29:"a:1:{s:1:"a";s:8:"String a";}";}}
print_r( $a);
print_r( $b);
/** Results:
A Object
(
[a:A:private] => String a
[b:A:private] =>
)
B Object
(
[c:B:private] => String c
[d:B:private] =>
[a:A:private] => String a
[b:A:private] =>
)
*/
总结总结:只有在超类中没有私有成员(需要序列化)时,才可以通过__sleep()
序列化类。您可以通过实现Serializable
接口来序列化复杂对象,但它会带来一些编程开销。
答案 1 :(得分:0)
您可以使用__sleep
and __wakeup
。对于前者,您提供了要序列化的对象属性名称的数组。忽略此列表中的“瞬态”成员。
__wakeup
。例如,您可以使用它来在某些条件下重新填充非瞬态属性。