2012-02-02 23 views
9

He trabajado con PHP desde hace unos años, pero hasta ahora nunca tuve la necesidad de tratar la serialización explícitamente, solo usando el $_SESSION. Ahora tengo un proyecto que requiere que implemente manualmente el mecanismo de serialización para ciertos datos, y me doy cuenta de que el problema también se aplica al $_SESSION.¿Propiedades "transitorias" en una clase de PHP?

Tengo una clase que contiene una serie de propiedades. La mayoría de estas propiedades son pequeñas (como en el consumo de memoria): números, cadenas relativamente cortas, etc. Sin embargo, la clase también contiene algunas propiedades, que pueden contener matrices ENORMES (por ejemplo, un volcado completo de una tabla de base de datos: 100.000 filas con 100 campos cada una) Da la casualidad de que esta es una de las clases que necesita ser serializada/deserializada y, afortunadamente, las propiedades que contienen grandes matrices no necesitan ser serializadas, ya que son esencialmente piezas temporales de trabajo y se reconstruyen de todos modos según sea necesario.

En tales circunstancias en Java, simplemente declararía la propiedad como transient - y se omitirá de la serialización. Desafortunadamente, PHP no es compatible con tales calificadores.

Una forma de tratar es que tenga algo como esto:

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; 
     } 
    } 
} 

Sin embargo, esto es bastante engorroso, ya que tendría que actualizar serialize método cada vez que agregar otra propiedad transitoria. Además, una vez que la herencia entra en juego, esto se vuelve aún más complicado, ya que las propiedades transitorias pueden estar tanto en la subclase como en la matriz. Lo sé, todavía es factible, sin embargo, preferiría delegar tanto como sea posible en el idioma en lugar de reinventar la rueda.

Entonces, ¿cuál es la mejor manera de lidiar con las propiedades transitorias? ¿O me estoy perdiendo algo y PHP lo admite de la caja?

Respuesta

7

Php proporciona __sleep magic method que le permite elegir qué atributos se serializarán.

EDITAR He probado cómo lo hace __sleep() trabajo cuando la herencia está en el juego:

<?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" 

Así que parece que se puede serializar los datos de los padres sólo si se declara como protegidos: -/

EDIT 2 lo he intentado con Serializable interfaz con el siguiente código:

<?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] => 
) 
*/ 

Así que para resumir: puede serializar las clases a través de __sleep() solo si no tienen miembros privados en la clase superior (que deben ser serializados también). Puede serializar objetos complejos a través de la implementación de la interfaz Serializable, pero le ofrece cierta sobrecarga de programación.

+1

'__sleep' no funcionará con propiedades privadas en la clase principal, por lo que solo será útil siempre que la herencia no esté involucrada. –

+0

@AleksG ha! ¡Tengo una solución que funciona! Echa un vistazo :) – Vyktor

+0

Gracias, parece un enfoque viable. Voy a experimentar un poco más con esto para ver cómo funciona. –

0

Puede usar __sleep and __wakeup. Para el primero, proporciona una matriz de los nombres de las propiedades de los objetos que desea serializar. Omita miembros "transitorios" de esta lista.

__wakeup se invoca inmediatamente cuando una instancia se deserializa. Puede usar esto para, por ejemplo, rellenar las propiedades no transitorias en algunas condiciones.

Cuestiones relacionadas