2011-08-05 33 views
5

Tôi đã gặp một vấn đề khá khó khăn để giải quyết các đối tượng thực hiện giao diện Serializable. Chúng ta hãy lấy một ví dụ:Duy trì các mối quan hệ đối tượng với việc tuần tự hóa PHP

class A { 
    public $b; 
} 

class B { 
    public $a; 
} 

$a = new A; 
$b = new B; 

$a->b = $b; 
$b->a = $a; 

echo serialize($a); // O:1:"A":1:{s:1:"b";O:1:"B":1:{s:1:"a";r:1;}} 
echo serialize($b); // O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"b";r:1;}} 

$a = unserialize(serialize($a)); 
var_export($a === $a->b->a); // true 

Chúng ta có thể thấy trong ví dụ này, rằng khi sử dụng tích hợp tính năng PHP serialization (đã hoặc chưa chúng tôi sử dụng __sleep() chức năng), các tài liệu tham khảo chéo giữa A & B được bảo quản (r:1;).

Tuy nhiên, nếu chúng ta muốn thực thi việc sử dụng các giao diện Serializable, vấn đề đặt ra:

class A implements Serializable { 
    public $b; 

    public function serialize() { return serialize($this->b); } 
    public function unserialize($s) { $this->b = unserialize($s); } 
} 

class B implements Serializable { 
    public $a; 

    public function serialize() { return serialize($this->a); } 
    public function unserialize($s) { $this->a = unserialize($s); } 
} 

$a = new A; 
$b = new B; 

$a->b = $b; 
$b->a = $a; 

echo serialize($a); // infinite loop crashes PHP 

Bởi vì mỗi đối tượng serialization được quản lý một cách độc lập, không có cách nào toàn cầu để xem liệu một đối tượng đã được được tuần tự hóa, để tạo một tham chiếu đến nó. Các hàm serialize() sau đó được gọi trong một vòng lặp vô hạn.

Có giải pháp tốt cho vấn đề này không? Một mẫu để sử dụng cho các chức năng serialize()?

+0

Đó là một tính năng PHP * mới "đẹp" * mà bạn tìm thấy ở đó. Mặc dù vậy, không thể cho bạn biết cách giải quyết. Tôi đã thử gán tham chiếu ('$ a-> b = & $ b'), nhưng điều đó cũng không thành công. –

+0

Đó là một hành vi hoàn toàn có thể dự đoán được, tôi nghĩ rằng giải pháp nằm trong mã người dùng, nhưng không thể tìm thấy cách thông minh để giải quyết vấn đề đó ngay bây giờ. – Benjamin

Trả lời

3

Vấn đề này đã được đặt tên tham chiếu đối tượng cyclic bởi Dự án Học thuyết trong tài liệu của họ trên Serializing entities:

Serializable không hoạt động tốt với bất kỳ tiềm năng cyclic object tài liệu tham khảo (ít nhất chúng tôi đã không tìm thấy một cách nào được nêu ra, nếu bạn đã làm, xin vui lòng liên hệ với chúng tôi).

Vì vậy, theo như tôi biết, hiện tại không có giải pháp chung cho vấn đề này.

3

Đây là một thủ thuật:

function inStack($cls, $func) { 
    $backTrace = debug_backtrace(); 
    for($i = 2; $i < count($backTrace); ++$i ) { 
     if(isset($backTrace[$i][ 'class' ]) && $backTrace[$i][ 'class' ] == $cls && 
      isset($backTrace[$i][ 'function' ]) && $backTrace[$i][ 'function' ] == $func) 
      return true; 
    } 
    return false; 
} 

class A implements Serializable { 
    public $b; 

    public function serialize() { 
     if(inStack('A', 'serialize')) return ''; 
     return serialize($this->b); 
    } 
    public function unserialize($s) { 
     if($s == '') return null; 
     $b = unserialize($s); 
     if($b !== null) { 
      $this->b = $b; 
      $this->b->a = $this; 
     } 
    } 
} 

class B implements Serializable { 
    public $a; 

    public function serialize() { 
     if(inStack('B', 'serialize')) return ''; 
     return serialize($this->a); 
    } 
    public function unserialize($s) { 
     if($s == '') return null; 
     $a = unserialize($s); 
     if($a !== null) { 
      $this->a = $a; 
      $this->a->b = $this; 
     } 
    } 
} 

$a = new A; 
$b = new B; 

$a->b = $b; 
$b->a = $a; 

$a = unserialize(serialize($a)); 
var_dump($a === $a->b->a); //true 
+0

Chà! Giải pháp kỹ thuật tốt, nhưng không phải là bài kiểm tra 'inStack()' của bạn chỉ dựa trên tên lớp, và sẽ phá vỡ nếu chúng ta phải tuần tự hóa nhiều hơn một cá thể của cùng một lớp? – Benjamin

+0

@Benjamin Khi chúng ta gọi 'inStack (' A ',' serialize ') 'từ bên trong' A :: serialize' nó sẽ chỉ trả về true khi chúng ta gọi A :: serialize đệ quy. Lưu ý rằng tôi lặp lại mảng dấu vết bắt đầu từ chỉ mục 2 để tự bỏ qua inStack() và lệnh A :: serialize hiện tại của chúng ta. – nobody

+0

Tôi vẫn không cảm thấy tự tin với việc sử dụng chức năng gỡ lỗi để cung cấp một hành vi ổn định: S – Benjamin

3

Tuần tự hóa chuẩn PHP cung cấp hai loại tham chiếu chỉ hoạt động cho mỗi lần gọi tuần tự hóa đơn lẻ theo cách tuần tự: đệ quy và đệ quy ref.

Khi bạn triển khai giao diện Serializable, bạn có khả năng có thể kéo dài điều này trên nhiều đối tượng và các bước tuần tự hóa miễn là bạn cung cấp một cửa hàng. Khi mỗi đối tượng trong bộ nhớ ví dụ có một định danh, một dịch vụ serilaization trung tâm có thể được nhồi với những ngày serialization và giải nén theo thứ tự khác nhau trên unserialization.

Dịch vụ có thể quan tâm đến việc quyết định tìm nạp đối tượng có nhiều tham chiếu đến nó từ cửa hàng - hoặc nếu đã unserialized - đối tượng thời gian chạy. Vì dịch vụ là trung tâm, nó có thể dễ dàng quyết định và các phương thức Serializable chỉ có thể sử dụng dịch vụ này.

Điều này cũng cho phép tham chiếu tuần hoàn. Tuy nhiên, bạn cần phải thực hiện điều này một mình vì ánh xạ.

Được thực hiện bởi từ, dự án giáo lý thực sự đã có sẵn điều này. Dạng tuần tự hóa đối tượng của nó là chính cơ sở dữ liệu. Chỉ để mở một cái nhìn, chắc chắn đây không phải là những gì bạn tìm kiếm.

Các vấn đề liên quan