PHP类中的“瞬态”属性?

时间:2012-02-02 14:51:27

标签: php class inheritance serialization

我已经使用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支持开箱即用?

2 个答案:

答案 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。例如,您可以使用它来在某些条件下重新填充非瞬态属性。