«Переходные» свойства в классе PHP? - PullRequest
9 голосов
/ 02 февраля 2012

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

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

7 голосов
/ 02 февраля 2012

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

РЕДАКТИРОВАТЬ Я проверял, как __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, но это приводит к некоторым программным издержкам.

0 голосов
/ 02 февраля 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...