2012-02-02 25 views
15

Tôi đang cố gắng triển khai chức năng tuần tự hóa/var_dump của riêng mình trong PHP. Có vẻ như không thể nếu có khả năng của mảng tròn (mà có).Có cách nào để phát hiện mảng tròn trong PHP thuần túy không?

Trong các phiên bản PHP gần đây, var_dump dường như phát hiện các mảng tròn:

php > $a = array(); 
php > $a[] = &$a; 
php > var_dump($a); 
array(1) { 
    [0]=> 
    &array(1) { 
    [0]=> 
    *RECURSION* 
    } 
} 

Làm thế nào tôi có thể thực hiện kiểu serialization riêng của tôi về phương pháp trong PHP có thể phát hiện tương tự? Tôi không thể theo dõi các mảng mà tôi đã truy cập, bởi vì việc so sánh chặt chẽ các mảng trong PHP trả về true cho các mảng khác nhau có chứa các phần tử giống nhau và so sánh các mảng tròn gây ra Lỗi nghiêm trọng, anyways.

php > $b = array(1,2); 
php > $c = array(1,2); 
php > var_dump($b === $c); 
bool(true) 
php > $a = array(); 
php > $a[] = &$a; 
php > var_dump($a === $a); 
PHP Fatal error: Nesting level too deep - recursive dependency? in php shell code on line 1 

Tôi đã tìm cách tìm id duy nhất (con trỏ) cho mảng, nhưng tôi không thể tìm thấy. spl_object_hash chỉ hoạt động trên các đối tượng, không phải mảng. Nếu tôi bỏ nhiều khác nhau mảng cho các đối tượng, tất cả chúng đều có cùng giá trị spl_object_hash (tại sao?).

EDIT:

Calling print_r, var_dump, hoặc serialize trên mỗi mảng và sau đó sử dụng một số cơ chế để phát hiện sự có mặt của đệ quy như phát hiện bởi các phương pháp đó là một phức tạp cơn ác mộng thuật toán và về cơ bản sẽ làm cho việc sử dụng quá chậm để thực tế trên các mảng lồng nhau lớn.

trả lời chấp nhận:

tôi chấp nhận câu trả lời dưới đây là người đầu tiên đề nghị tạm thời thay đổi một mảng để xem nếu nó thực sự là giống như mảng khác. Điều đó trả lời "làm thế nào để tôi so sánh hai mảng cho danh tính?" từ đó phát hiện đệ quy là tầm thường.

+2

Câu trả lời là sẽ là: bạn không thể. Xem [check if object/array là một tham chiếu] (http://stackoverflow.com/questions/3148125/php-check-if-object-array-is-a-reference). Không có so sánh tham chiếu giống như con trỏ, vì vậy việc phát hiện một vòng lặp không thể thực hiện được. Cách giải quyết tốt hơn trong trường hợp của bạn có thể là truyền xung quanh một trong các hàm gốc ('json_decode (json_encode())') để loại bỏ các tham chiếu, và chỉ sau đó áp dụng điều kiện tuần tự hóa của riêng bạn. – mario

+1

Bây giờ ngay cả PHPUnit đang sử dụng phương pháp "đánh dấu" tạm thời để phát hiện đệ quy mảng. – postfuturist

Trả lời

4

Phương thức isRecursiveArray (mảng) dưới đây phát hiện mảng tròn/đệ quy. Nó theo dõi các mảng nào đã được truy cập bằng cách tạm thời thêm một phần tử chứa tham chiếu đối tượng đã biết vào cuối mảng.

Nếu bạn muốn được trợ giúp viết phương pháp tuần tự hóa, vui lòng cập nhật câu hỏi chủ đề của bạn và cung cấp định dạng tuần tự hóa mẫu trong câu hỏi của bạn.

function removeLastElementIfSame(array & $array, $reference) { 
    if(end($array) === $reference) { 
     unset($array[key($array)]); 
    } 
} 

function isRecursiveArrayIteration(array & $array, $reference) { 
    $last_element = end($array); 
    if($reference === $last_element) { 
     return true; 
    } 
    $array[] = $reference; 

    foreach($array as &$element) { 
     if(is_array($element)) { 
      if(isRecursiveArrayIteration($element, $reference)) { 
       removeLastElementIfSame($array, $reference); 
       return true; 
      } 
     } 
    } 

    removeLastElementIfSame($array, $reference); 

    return false; 
} 

function isRecursiveArray(array $array) { 
    $some_reference = new stdclass(); 
    return isRecursiveArrayIteration($array, $some_reference); 
} 



$array  = array('a','b','c'); 
var_dump(isRecursiveArray($array)); 
print_r($array); 



$array  = array('a','b','c'); 
$array[] = $array; 
var_dump(isRecursiveArray($array)); 
print_r($array); 



$array  = array('a','b','c'); 
$array[] = &$array; 
var_dump(isRecursiveArray($array)); 
print_r($array); 



$array  = array('a','b','c'); 
$array[] = &$array; 
$array  = array($array); 
var_dump(isRecursiveArray($array)); 
print_r($array); 
+1

Tôi nghĩ rằng tạm thời thay đổi mảng có lẽ là chỉ có cách hợp lý để kiểm tra xem hai mảng có giống nhau không. Đây là câu trả lời đầu tiên cho thấy cách tiếp cận đó, vì vậy tôi sẽ chấp nhận nó. – postfuturist

-1

Nó không thanh lịch, nhưng giải quyết vấn đề của bạn (ít nhất là nếu bạn không có ai đó sử dụng * RECURSION * làm giá trị).

<?php 
$a[] = &$a; 
if(strpos(print_r($a,1),'*RECURSION*') !== FALSE) echo 1; 
+1

Chỉ không hoạt động nếu bạn tình cờ có chuỗi '" * RECURSION * "' ở bất kỳ đâu trong mảng của bạn ... Phải thừa nhận là hiếm, nhưng có thể. – deceze

+0

Yeap, đó là ý của tôi khi nói về \ * RECURSION \ * làm giá trị. Dù sao bạn có thể thêm một kiểm tra cho trường hợp đó iterating mảng và kiểm tra giá trị đó và bạn có một giới hạn của iterations dựa trên bao nhiêu dòng print_r đầu ra đã cho bạn. – dvicino

+0

Vâng, điều đó phát hiện (một cách cực kỳ không hiệu quả) rằng có đệ quy _somewhere_ có lẽ ... không thực sự giải quyết trường hợp sử dụng của tôi. – postfuturist

0

Phương thức hài hước (Tôi biết nó ngu ngốc :)), nhưng bạn có thể sửa đổi và theo dõi "đường dẫn" đến phần tử đệ quy. Đây chỉ là một ý tưởng :) Dựa trên thuộc tính của chuỗi được tuần tự hóa, khi đệ quy bắt đầu bằng sẽ giống như chuỗi cho mảng ban đầu. Như bạn có thể thấy - Tôi đã thử nó trên nhiều biến thể khác nhau và có thể là một cái gì đó có thể 'đánh lừa' nó, nhưng nó 'phát hiện' tất cả các cuộc truy tìm được liệt kê. Và tôi đã không thử các mảng đệ quy với các đối tượng.

$a = array('b1'=>'a1','b2'=>'a2','b4'=>'a3','b5'=>'R:1;}}}'); 
$a['a1'] = &$a; 
$a['b6'] = &$a; 
$a['b6'][] = array(1,2,&$a); 
$b = serialize($a); 
print_r($a); 
function WalkArrayRecursive(&$array_name, &$temp){ 
    if (is_array($array_name)){ 
     foreach ($array_name as $k => &$v){ 
      if (is_array($v)){ 
       if (strpos($temp, preg_replace('#R:\d+;\}+$#', '', 
           serialize($v)))===0) 
       { 
        echo "\n Recursion detected at " . $k ."\n"; 
        continue; 
       } 
       WalkArrayRecursive($v, $temp); 
      } 
     } 
    } 
} 
WalkArrayRecursive($a, $b); 

regexp là cho trường hợp khi phần tử đệ quy ở cuối 'của mảng. và, có, đệ quy này có liên quan đến toàn bộ mảng. Có thể thực hiện đệ quy các phần tử con, nhưng đã quá muộn để tôi suy nghĩ về chúng. Bằng cách nào đó mọi phần tử của mảng phải được kiểm tra cho đệ quy trong các phần tử con của nó. Tương tự, như trên, thông qua đầu ra của hàm print_r, hoặc tìm kiếm bản ghi cụ thể cho đệ quy trong chuỗi được tuần tự hóa (R:4;} một cái gì đó như thế này). Và truy tìm nên bắt đầu từ yếu tố đó, so sánh mọi thứ bên dưới bằng kịch bản của tôi. Tất cả chỉ là nếu bạn muốn phát hiện nơi đệ quy bắt đầu, không chỉ cho dù bạn có nó hay không.

ps: nhưng điều tốt nhất nên, như tôi nghĩ, để viết chức năng unserialize của riêng bạn từ chuỗi serailized được tạo ra bởi php chính nó.

0

Phương pháp tiếp cận của tôi là có một bản sao tạm thời chứa bản sao của tất cả các đối tượng đã được lặp lại. như thế này đây:

// We use this to detect recursion. 
global $recursion; 
$recursion = []; 

function dump($data, $label, $level = 0) { 
    global $recursion; 

    // Some nice output for debugging/testing... 
    echo "\n"; 
    echo str_repeat(" ", $level); 
    echo $label . " (" . gettype($data) . ") "; 

    // -- start of our recursion detection logic 
    if (is_object($data)) { 
     foreach ($recursion as $done) { 
      if ($done === $data) { 
       echo "*RECURSION*"; 
       return; 
      } 
     } 

     // This is the key-line: Remember that we processed this item! 
     $recursion[] = $data; 
    } 
    // -- end of recursion check 

    if (is_array($data) || is_object($data)) { 
     foreach ((array) $data as $key => $item) { 
      dump($item, $key, $level + 1); 
     } 
    } else { 
     echo "= " . $data; 
    } 
} 

Và đây là một số code demo nhanh chóng để minh họa cách hoạt động:

$obj = new StdClass(); 
$obj->arr = []; 
$obj->arr[] = 'Foo'; 
$obj->arr[] = $obj; 
$obj->arr[] = 'Bar'; 
$obj->final = 12345; 
$obj->a2 = $obj->arr; 

dump($obj, 'obj'); 

Kịch bản này sẽ tạo ra kết quả như sau:

obj (object) 
    arr (array) 
    0 (string) = Foo 
    1 (object) *RECURSION* 
    2 (string) = Bar 
    final (integer) = 12345 
    a2 (array) 
    0 (string) = Foo 
    1 (object) *RECURSION* 
    2 (string) = Bar 
Các vấn đề liên quan