추상 구문 트리란 무엇인가요?
추상 구문 트리(AST)는 소스 코드의 추상 구문 구조를 트리로 표현한 것입니다. 트리의 각 노드는 소스 코드의 구조를 나타내지 않기 때문에 추상이라고 합니다. 예를 들어, 중첩된 괄호는 트리 구조에 암시되어 있으며 노드로 표시되지 않습니다. 추상 구문 트리는 소스 언어의 문법에 의존하지 않습니다. 즉 구문 분석 단계에서 사용되는 문맥 자유 문법을 의미합니다. [문법은 언어의 문법 구조를 설명하는 데 사용되는 형식적인 규칙입니다. 기계어든 자연어든 모든 언어에는 고유한 문법이 있습니다. ], 왜냐하면 문법을 작성할 때 문법에 대해 등가 변환이 종종 수행되기 때문입니다(왼쪽 재귀 제거, 역추적, 모호성 등). 이는 문법 분석에 일부 중복 구성 요소를 도입하고 후속 단계에 부정적인 영향을 미치며 심지어 무대 전체가 혼란스럽다. 이러한 이유로 많은 컴파일러는 프런트 엔드와 백 엔드에 대한 명확한 인터페이스를 구축하기 위해 독립적으로 구문 분석 트리를 구성해야 하는 경우가 많습니다. PHP-Parser의 프로젝트 홈페이지는 https://github.com/nikic/PHP-Parser입니다. 여러 버전의 PHP를 완벽하게 구문 분석하고 추상 구문 트리를 생성할 수 있습니다.
새로운 실행 프로세스PHP7 핵심의 중요한 변화는 AST의 추가입니다. PHP5에서 PHP 스크립트에서 opcode까지의 실행 프로세스는 다음과 같습니다.
1.Lexing: 어휘 스캐닝 분석, 소스 파일을 토큰 스트림으로 변환
2.Parsing: 구문 분석, 이 단계에서 op 배열 생성.
PHP7에서는 더 이상 구문 분석 단계에서 op 배열이 직접 생성되지 않지만 AST가 먼저 생성되므로 프로세스에 한 단계가 더 있습니다:
1 렉싱: 어휘 스캐닝 분석, 소스 파일을 토큰 스트림으로 변환. ;
2.파싱: 구문 분석, 토큰 스트림에서 추상 구문 트리 생성
3.컴파일: 추상 구문 트리에서 연산 배열 생성.
실행 시간 및 메모리 소모위 단계에서 이전 과정보다 한 단계 더 진행되므로 상식적으로 보면 프로그램의 실행 시간과 메모리 사용량이 늘어나게 됩니다. 하지만 실제로는 메모리 사용량이 늘어난 것은 사실이지만 실행 시간은 줄어들었습니다.
소형(코드 약 100라인), 중형(약 700라인), 대형(약 2800라인) 세 가지 스크립트를 테스트하여 얻은 결과는 다음과 같습니다. 테스트 스크립트: https://gist.github.com/nikic /289b0c7538b46c2220bc.
각 파일을 100번 컴파일하는 실행 시간(기사의 테스트 결과 시간은 PHP7이 여전히 PHP-NG로 불렸던 당시 14년입니다):
단일 컴파일의 최대 메모리:
단일 컴파일의 테스트 결과는 실제 사용법을 나타내지 않을 수 있습니다. 다음은 PhpParser를 사용한 전체 프로젝트 테스트의 결과입니다.
테스트에 따르면 AST를 사용한 후 프로그램의 전체 실행 시간은 다음과 같습니다. 약 10~15% 정도 개선되지만 메모리 소모도 증가합니다. 대용량 파일을 한 번 컴파일할 때 이러한 증가는 뚜렷하지만 전체 프로젝트 실행 중에는 심각한 문제가 아닙니다.
또한 위의 결과는 모두 Opcache를 사용하지 않은 결과이므로 프로덕션 환경에서 Opcache를 켜면 메모리 소비 증가는 큰 문제가 되지 않습니다.
Semantic Changes단순한 시간 최적화라면 AST를 사용하는 충분한 이유가 되지 않는 것 같습니다. 실제로 AST 구현은 시간 최적화 고려사항이 아니라 구문 문제를 해결하기 위한 것입니다. 의미론의 몇 가지 변화를 살펴보겠습니다.
yield에는 괄호가 필요하지 않습니다.PHP5 구현에서 표현식 컨텍스트(예: 할당 표현식의 오른쪽)에서 Yield를 사용하는 경우 Yield 선언 주위에 괄호를 사용해야 합니다.
<?php $result = yield fn(); // 不合法的 $result = (yield fn()); // 合法的
This 이 동작은 단순히 PHP5의 구현 제한으로 인한 것입니다. PHP7에서는 괄호가 더 이상 필요하지 않습니다. 따라서 다음 작성 방법도 적법합니다.
<?php $result = yield; $result = yield $v; $result = yield $k => $v;
물론 Yield의 적용 시나리오를 따라야 합니다.
괄호는 동작에 영향을 주지 않습니다PHP5에서는
<?php ($foo)['bar'] = 'baz'; # PHP Parse error: Syntax error, unexpected '[' on line 1
하지만 PHP7에서는 두 가지 쓰기 방법이 같은 의미입니다.
마찬가지로 함수의 매개변수가 괄호로 묶인 경우 유형 검사에 문제가 있습니다. 이 문제는 PHP7에서도 해결되었습니다.
<?php function func() { return []; } function byRef(array &$a) { } byRef((func()));
위 코드는 byRef(func()를 호출하지 않는 한 PHP5에서 경보를 울리지 않습니다. ) ) 이지만 PHP7에서는 func() 양쪽에 괄호 유무에 관계없이 다음 오류가 발생합니다.
PHP Strict standards: Only variables should be passed by reference ...
list 关键字的行为改变了很多。list 给变量赋值的顺序(等号左右同时的顺序)以前是从右至左,现在是从左到右: 产生上面变化的原因正是因为在 PHP5 的赋值过程中,3 会最先被填入数组,1 最后,但是现在顺序改变了。 同样的变化还有: 这是因为在以前的赋值过程中 $b 先得到 2,然后 $a 的值才变成1,但是现在 $a 先变成了 1,不再是数组,所以 $b 就成了null。 list 现在只会访问每个偏移量一次 空的 list 成员现在是全部禁止的,以前只是在某些情况下: 引用赋值的顺序 引用赋值的顺序在 PHP5 中是从右到左的,现在时从左到右: __clone 方法可以直接调用 现在可以直接使用 $obj->__clone() 的写法去调用 __clone 方法。 __clone 是之前唯一一个被禁止直接调用的魔术方法,之前你会得到一个这样的错误: 变量语法一致性 AST 也解决了一些语法一致性的问题,这些问题是在另外一个 RFC 中被提出的:https://wiki.php.net/rfc/uniform_variable_syntax. 在新的实现上,以前的一些语法表达的含义和现在有些不同,具体的可以参照下面的表格: 整体上还是以前的顺序是从右到左,现在从左到右,同时也遵循括号不影响行为的原则。这些复杂的变量写法是在实际开发中需要注意的。 相关推荐:《PHP教程》 위 내용은 PHP7의 추상 구문 트리(AST)로 인한 변경 사항의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!
<?php
list($array[], $array[], $array[]) = [1, 2, 3];
var_dump($array);
// PHP5: $array = [3, 2, 1]
// PHP7: $array = [1, 2, 3]
# 注意这里的左右的顺序指的是等号左右同时的顺序,
# list($a, $b) = [1, 2] 这种使用中 $a == 1, $b == 2 是没有疑问的。
<?php
$a = [1, 2];
list($a, $b) = $a;
// PHP5: $a = 1, $b = 2
// PHP7: $a = 1, $b = null + "Undefined index 1"
<?php
list(list($a, $b)) = $array;
// PHP5:
$b = $array[0][1];
$a = $array[0][0];
// PHP7:
// 会产生一个中间变量,得到 $array[0] 的值
$_tmp = $array[0];
$a = $_tmp[0];
$b = $_tmp[1];
<?php
list() = $a; // 不合法
list($b, list()) = $a; // 不合法
foreach ($a as list()) // 不合法 (PHP5 中也不合法)
<?php
$obj = new stdClass;
$obj->a = &$obj->b;
$obj->b = 1;
var_dump($obj);
// PHP5:
object(stdClass)#1 (2) {
["b"] => &int(1)
["a"] => &int(1)
}
// PHP7:
object(stdClass)#1 (2) {
["a"] => &int(1)
["b"] => &int(1)
}
Fatal error:Cannot call __clone() method on objects -use 'clone $obj' instead in...