ホームページ  >  記事  >  バックエンド開発  >  新しい抽象構文ツリー (AST) によって PHP7 に加えられた変更

新しい抽象構文ツリー (AST) によって PHP7 に加えられた変更

Guanhui
Guanhui転載
2020-05-14 11:12:073041ブラウズ

新しい抽象構文ツリー (AST) によって PHP7 に加えられた変更

この記事の内容の大部分は、AST の RFC ドキュメントに基づいています: https://wiki.php.net/rfc/abstract_syntax_tree , 理解を容易にするために、ソース文書からの抜粋が紹介されています。

この記事では、抽象構文ツリーが何であるかについては説明しません。これは自分で理解する必要があります。この記事では、AST が PHP にもたらすいくつかの変更についてのみ説明します。

新しい実行プロセス

PHP7 のコアにおける重要な変更は、AST の追加です。 PHP5 では、PHP スクリプトからオペコードまでの実行プロセスは次のとおりです:

1. 字句解析: 字句スキャン分析、ソース ファイルをトークン ストリームに変換します;

2. 解析: 構文分析、Op 配列はこの段階で生成されます。

3. PHP7 では、op 配列は構文分析段階で直接生成されなくなりましたが、AST が最初に生成されるため、プロセスにはもう 1 つのステップがあります:

4. 字句解析:字句スキャン分析、ソース ファイルをトークン ストリームに変換;

5. 解析: 構文分析、トークン ストリームから抽象構文ツリーを生成;

6. コンパイル: 抽象から op 配列を生成構文ツリー。

実行時間とメモリ消費量

上記の手順から、これは前のプロセスよりも 1 つの手順が多いため、常識的には、これによりプログラムの実行時間が増加します。時間とメモリの使用量。しかし実際には、メモリ使用量は確かに増加していますが、実行時間は減少しています。

次の結果は、小規模 (約 100 行のコード)、中規模 (約 700 行)、および大規模 (約 2800 行) の 3 つのスクリプトをテストして得られます。テスト スクリプト: https ://gist.github.com/nikic/289b0c7538b46c2220bc

各ファイルを 100 回コンパイルする実行時間 (記事のテスト結果時間は 14 年であることに注意してください、PHP7 とも呼ばれます) PHP-NG ):

#SMALL0.180s0.160s-12.5%MEDIUM1.492s1.268s-17.7%LARGE6.703s 5.736s-16.9%1 回のコンパイルでのメモリ ピーク:

##php-ng
php-ast diff


#php-ng##SMALL378kB414kB 9.5%643kB1857kB単一コンパイルのテスト結果は、実際の使用状況を表していない可能性があります。以下は、PhpParser を使用した完全なプロジェクト テストの結果です:

php-ast
diff
##MEDIUM 507kB
26.8% 1084kB
71.3%

##php-ngphp-ast 時間25.5ms22.8ms-11.8% 5.1%##

テストによると、AST を使用した後、プログラムの全体的な実行時間は約 10% ~ 15% 改善されましたが、メモリ消費量も増加しました。この増加は、大きなファイルの 1 回のコンパイルでは明らかですが、実行中はそうではありません。プロジェクトの実行プロセス全体が非常に深刻な問題です。

また、上記の結果はすべて Opcache を使用していないことにも注意してください。運用環境で Opcache がオンになっている場合、メモリ消費量の増加は大きな問題ではありません。

セマンティックな変更

これが単なる時間の最適化である場合、AST を使用する十分な理由にはならないようです。実際、AST の実装は時間の最適化を考慮したものではなく、構文の問題を解決するために行われています。セマンティクスにおけるいくつかの変更を見てみましょう。

yield には括弧は必要ありません

PHP5 実装では、式コンテキスト (代入式の右側など) で yield を使用する場合、 yield 宣言の両側で括弧を使用する必要があります:

<?php
$result = yield fn();   // 不合法的
$result = (yield fn()); // 合法的

この動作は、PHP5 の実装制限によるものです。PHP7 では、括弧は必要なくなりました。したがって、次の記述方法も合法です:

<?php
$result = yield;
$result = yield $v;
$result = yield $k => $v;

もちろん、yield のアプリケーション シナリオに従わなければなりません。

括弧は動作に影響しません

PHP5 では、($foo)['bar'] = 'baz' および $foo['bar'] = 'baz ' 2 つのステートメントの意味は異なります。実は、前者の書き方は違法であり、次のようなエラーが発生します。

<?php
($foo)[&#39;bar&#39;] = &#39;baz&#39;;
# PHP Parse error: Syntax error, unexpected &#39;[&#39; on line 1

しかし、PHP7 では、2 つの書き方は同じ意味になります。

同様に、関数のパラメータが括弧で囲まれている場合、型チェックに問題があります。この問題は PHP7 でも解決されています:

<?php
function func() {
    return [];
}

function byRef(array &$a) {
}

byRef((func()));

上記のコードは、次の場合には警告しません。 PHP5 では byRef が使用されていない限り (func())、PHP7 では func() の両側に括弧があるかどうかに関係なく、次のエラーが発生します。

PHP Strict standards: Only variables should be passed by reference ...

Changes in list()

list キーワードの動作が大幅に変更されました。リストが変数に値を割り当てる順序 (等号の左右の順序) は、以前は右から左でしたが、現在は左から右です:

<?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 是没有疑问的。

上記の変更の理由は、正確には、PHP5 の代入プロセスでは、最初に 3 が配列に入力され、最後に 1 が入力されるためですが、順序が変更されたためです。

同じ変更は次のとおりです:

<?php
$a = [1, 2];
list($a, $b) = $a;

// PHP5: $a = 1, $b = 2
// PHP7: $a = 1, $b = null + "Undefined index 1"

これは、前の代入プロセスで $b が最初に 2 を取得し、その後 $a の値が 1 になったためです。しかし、今回は $a が最初に変更されます。は 1 になり、配列ではなくなるため、$b は null になります。

list はオフセットごとに 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 中也不合法)

参照の順序assign

参照割り当ての順序は、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)
}

#clone メソッドは直接呼び出し可能です

これで、$obj->__clone() を直接使用して __clone メソッドを呼び出すことができます。 __clone は、以前は直接呼び出すことが禁止されていた唯一のマジック メソッドでした。以前は、次のようなエラーが発生していました:

Fatal error: Cannot call __clone() method on objects - use &#39;clone $obj&#39; instead in ...

変数構文の一貫性

AST もいくつかの問題を解決しました構文の一貫性の問題。これらの問題は別の RFC で提起されました: https://wiki.php.net/rfc/uniform_variable_syntax.

新しい実装では、以前の文法表現の意味は現在とは多少異なります。詳細については、次の表を参照してください:

diff
##メモリ 2360kB 2482kB
##$foo->{$bar['baz']} ##$foo->$ bar['baz' ]()推奨チュートリアル:「PHP7
PHP5 PHP7
$foo['bar']['baz'] #$ {$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] #($foo->$bar)['baz']
$foo->{$bar['baz']}() #($ foo->$bar )['baz']() ##Foo::$bar['baz']()
Foo ::{$bar['baz']}() (Foo::$bar)['baz']() 全体的に、以前の順序は右から左でしたが、現在は左から右です。また、括弧は動作に影響を与えないという原則に従います。このような複雑な変数の書き方は、実際の開発では注意が必要です。

以上が新しい抽象構文ツリー (AST) によって PHP7 に加えられた変更の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。