순수한 PHP에서 순환 배열을 감지할 수 없는 이유
겸사겸사 한마디 하자면
순환수 그룹을 신뢰할 수 있는 해결 방안을 찾고 있을 뿐이고, 쿨한 PHP 괴벽을 놓치고 싶다면, 이 시리즈의 두 번째 부분으로 넘어가십시오.
우선, 우리의 용어를 정의합시다.이 글에서 우리는 매우 실용적인 정의를 지원할 수 있다. 수조는 순환적이며, 모든 항목과 하위 항목을 교체하는 것이 끝나지 않는다면.순환수조는 때때로 순환수조 또는 귀속수조라고도 부른다.몇 가지 예를 살펴보겠습니다.
// Contains a reference to itself
$v = [1, 2, 3];
$v[1] = &$v;
// Contains a nested array that contains a reference to $x
$x = [1, [2, 3]];
$x[1][1] = &$x;
// Contains a nested array that contains a reference to an ancestor
$y = [1, [2, [3, 4]]];
$y[1][1][1] = &$y[1];
// Contains a nested array that contains a reference to itself
$z = [1, [2, 3]];
$z[1][1] = &$z[1];
순수한 PHP 솔루션 시도
다음은 순환 수조를 검출하는 간단한 알고리즘입니다. 주어진 수조에 대해 모든 항목과 하위 항목을 교체하고 모든 수조를 표시합니다.만약 당신이 표시된 그룹을 만났을 때, 순환을 찾았고, 입력된 그룹은 순환합니다.PHP를 사용하여 작성한 방법은 다음과 같습니다.
function is_cyclic(array &$array)
{
    $lastKey = array_key_last($array);
    if ($lastKey === null) {
        // Array is empty
        return false;
    }
    static $marker;
    if ($marker === null) {
        $marker = new stdClass();
    }
    if ($array[$lastKey] === $marker) {
        return true;
    }
    $array[] = $marker;
    foreach ($array as &$item) {
        if (is_array($item) && is_cyclic($item)) {
            array_pop($array); // Remove marker
            return true;
        }
    }
    array_pop($array); // Remove marker
    return false;
}
순수한 PHP 솔루션의 혁신
이 절의 첫머리에서 내가 말하고자 하는 것은 PHP에서 대상을 대하는 것처럼 수조의 실례 표식을 비교할 수 없다는 것이다.두 변수가 같은 그룹을 인용하는지 확인하는 유일한 방법은 하나의 변수로 그룹을 표시한 다음에 다른 변수로 표시를 검사하는 것이다.따라서 만약에 우리가 이 과정에서 관건적인 결함을 찾을 수 있다면 순수 PHP에서 순환수 그룹을 신뢰할 수 없을 것이다.
이전의 기능을 깨뜨리기 위해서, 우리는 그것을 위해 정성들여 만든 수조를 제공해야 한다.이 점을 해낼 수 있는 몇 가지 방법이 있다.내가 너희들에게 보여준 그 것은 보기에 매우 믿을 수 없는 것 같아서, 아마도 실제 코드에 나타날 것이다.
function craft_bomb() {
    $array = [1, [2, 3]];
    $array[1][1] = &$array;
    return $array;
}
$bomb = craft_bomb();
var_export(is_cyclic($bomb));
var_dump. 알려 드리겠습니다.$bomb = craft_bomb();
var_dump($bomb);
array(2) {
  [0]=>
  int(1)
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    *RECURSION*
  }
}
$local = [1, [2, 3]];
$local[1][1] = &$local;
var_dump($local);
array(2) {
  [0]=>
  int(1)
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    *RECURSION*
  }
}
실행 코드 어레이 내부에 무슨 일이 일어났는지
사진 작성자
 출처cottonbro
 출처cottonbro프로그램에서 실제로 무슨 일이 일어났는지 알아보기 위해 printf 디버깅을 진행합시다.호출 시 함수 매개 변수를 덤프할 때마다 다음과 같은 출력이 제공됩니다(Pexels.
Call 1:
array(2) {
  [0]=>
  int(1)
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    _RECURSION_
  }
}
Call 2:
array(2) {
  [0]=>
  int(2)
  [1]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    _RECURSION_
  }
}
Call 3:
Same as call 1
Call 4:
Same as call 2
...
함수 매개 변수보다 더 통찰력이 있는 것은 원시 입력 수조의 변화이다.모든 함수에 덤프를 호출할 때, 우리는 매우 이상한 것들을 볼 수 있다. (run code
Call 1:
array(2) {
  [0]=>
  int(1)
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    _RECURSION_
  }
}
Call 2:
array(3) {
  [0]=>
  int(1)
  [1]=>
  &array(2) {
    [0]=>
    int(2)
    [1]=>
    array(2) {
      [0]=>
      int(1)
      [1]=>
      _RECURSION_
    }
  }
  [2]=>
  object(stdClass)#1 (0) {
  }
}
Call 3:
array(3) {
  [0]=>
  int(1)
  [1]=>
  &array(3) {
    [0]=>
    int(2)
    [1]=>
    &array(2) {
      [0]=>
      int(1)
      [1]=>
      array(2) {
        [0]=>
        int(2)
        [1]=>
        _RECURSION_
      }
    }
    [2]=>
    object(stdClass)#1 (0) {
    }
  }
  [2]=>
  object(stdClass)#1 (0) {
  }
}
...
우리는 왜 수조가 이런 방식으로 운행되는지, 왜 그것이 로컬 구조의 수조와 같지 않은지 여전히 모른다.
실행 코드 배열을 신뢰할 수 없습니다.
사진 작성자
 출처Sharath G.
 출처Sharath G.일반적으로 수조의 작업 방식은 사람들이 예상한 것과 같다.그러나 인용을 포함하는 그룹 실행 값을 사용하면 예측성이 사라집니다."왜?"물어볼 수도 있어.이 코드를 보고 원하는 출력을 고려하십시오 (Pexels:
$a = 1;
$b = [&$a];
$c = $b;
$b[0] = 2;
echo "$b[0] $c[0]"; // 2 2
2 2라고 말한다면, 너는 옳다.그러나 이것은 2 1일 수도 있고 프로그램의 나머지 부분에 달려 있다. 예를 들어 run code:$a = 1;
$b = [&$a];
$c = $b;
unset($a); // <--
$b[0] = 2;
echo "$b[0] $c[0]"; // 2 1
$a = 1;
$b = [&$a]; // $b[0] is an alias for $a
$c = $b;
// The last line is equivalent to either:
// $c = [&$a] or $c = [$a];
$a = 1;
$b = [&$a];
$c = $b;
// The last line might be sort of equivalent to $c = &$b
// until the array is mutated through either $b or $c.
is_cyclic가 국부 작용역에서 구성된 수조와 함수가 되돌아오는 수조의 행위가 다른지 합리적으로 해석할 수 있다.첫 번째 상황에서, 수조에 대해 어떠한 값 부여도 실행하지 않았는데, 이것이 바로 함수의 행동이 예상과 일치하는 이유이다.
두 번째 상황에서 수조는 함수에서 값에 따라 되돌아와 국부 변수에 분배된다.복제가 지연되었습니다(규칙 2).우리가 그룹을 표시하려고 할 때, 실행할 때, 먼저 복사본을 만들고, 이 복사본은 표시를 받을 것이다. (규칙 2)복제를 실행하는 데는 복제 플러그인 그룹도 포함됩니다. 이것은 다시 지연되고 반복됩니다.매번 우리가 하나의 그룹을 표시할 때마다 새로운 복사본을 생성하고, 초기 그룹은 더욱 깊어진다.
나는 이것이 이해하기 어려울 수도 있다는 것을 알고 chapter 4 of the PHP Language Specification를 만들어서 이 장면의 추상적인 메모리 모델을 설명했다.그것은 대체로 diagram의 메모리 모델 표현법을 따른다.
PHP 언어 사양 이 이야기의 사기
별명을 포함하는 그룹을 복제하는 것을 최대한 피하십시오.이 동작은 실행할 때에 의존하며, 이 동작은 당신이 원하는 것이 아닐 수도 있습니다.PHP 언어 사양 참조:
For portability, it is generally recommended that programs written in PHP should avoid performing value assignment with a right-hand side that is an array with one or more elements or sub-elements that have an alias relationship.
PHP Language Specification, https://phplang.org/spec/04-basic-concepts.html
마무리
내가 처음에 말했듯이, 신뢰할 수 있는 순환 수조를 검출하는 해결 방안에 관심이 있다면, 이것은 매우 간단할 것이라고 보장합니다. 그러면 이 시리즈를 보십시오.
너는 이 문장 에서 모든 코드 예시를 찾을 수 있다.
GitHub repository
관련 작업:
Reference
이 문제에 관하여(순수한 PHP에서 순환 배열을 감지할 수 없는 이유), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hbgl/why-it-is-impossible-to-detect-cyclic-arrays-in-pure-php-oon텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)