2012-02-02 2 views
9

Я работал с PHP уже несколько лет, но до сих пор мне не приходилось иметь дело с сериализацией явно, используя только $_SESSION. Теперь у меня есть проект, который требует от меня вручную реализовать механизм сериализации для определенных данных - и я понимаю, что проблема применима и к $_SESSION."Переходные" свойства в классе PHP?

У меня есть класс, который содержит ряд свойств. Большинство из этих свойств малы (как в потреблении памяти): числа, относительно короткие строки и т. Д. Однако класс также содержит некоторые свойства, которые могут содержать массивы 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 поддерживает это из коробки?

ответ

7

Php предоставляет __sleep magic method, который позволяет вам выбирать, какие атрибуты должны быть сериализованы.

EDIT Я проверил, как делает __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" 

Таким образом, кажется, что вы можете сериализовать родительские данные, только если он объявлен как защищенный: -/

EDIT 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

'__sleep' не будет работать с приватными свойствами в родительском классе, поэтому это полезно только при условии, что наследование не задействовано. –

+0

@AleksG ha! У меня есть решение, которое работает! Взгляните :) – Vyktor

+0

Спасибо, это похоже на жизнеспособный подход. Я немного экспериментирую с этим, чтобы посмотреть, как это работает. –

0

Вы можете использовать __sleep and __wakeup. Для первого вы предоставляете массив имен свойств объектов, которые вы хотите сериализовать. Опустите «переходные» члены из этого списка.

__wakeup вызывается немедленно, когда экземпляр неэтериализован. Вы можете использовать это, например, для пополнения непереходных свойств при некоторых условиях.

Смежные вопросы