SlideShare une entreprise Scribd logo
1  sur  77
2016/11/03
PHP Conference Japan 2016
do_aki
1
updated 2016-12-13
@do_aki
@do_aki
http://do-aki.net/
第1章
php のコンパイラ
PHP
Compiler in PHP
PHP Script
Opcode
Request
Output
Compiler
Lexing
Parsing
Compilation
VM
Execution
INCLUDE_OR_EVAL
コンパイルの流れ
字句解析 構文解析 Opcode生成
狭義のコンパイル
AST を生成
トークンに分解
Lexical Analysis
字句解析
• ソースコードをトークン(意味を持つ最
小の単位)に分解
• token_get_all 関数 で確認できる
• 余談: token_get_all の中では実際にコ
ンパイル処理が行われる(ただし、
Opcode 生成は省略)
ソースコード(php スクリプト)
<?php
function hello ( $name ) {
echo “HELLO $name“ ;
}
hello ( “php“ ) ;
字句解析
<?php
function hello ( $name ) {
echo “HELLO $name“ ;
}
hello ( “php“ ) ;
T_FUNCTION T_STRING ( ) {
}
T_ECHO
T_ENCAPSED_AND
_WHITESPACE ;
T_STRING ( ) ;
T_OPEN_TAG
T_VARIABLE
T_VARIABLE
T_VARIABLE“ “
ひとつひとつがトークン
(意味を持つ最小の単位)
Syntax Analysis
構文解析
• トークンの並びから構文を導く
• 構文に応じたASTを構築する
• 該当する構文が見つからないときは
Parse Error (7.0 から Exception)
構文解析
T_FUNCTION T_STRING ( ) {
}
T_ECHO T_ENCAPSED_AND_WHITESPACE ;
T_STRING ( ) ;
T_OPEN_TAG
T_VARIABLE
T_VARIABLE
function declaration
function call
AST構築
(by do-aki/phpast)
Bytecode Generation
Opcode生成
• AST を解析して Opcode を生成
• 狭義のコンパイル(zend_compile.c)
• いくつかの最適化が施される(後述)
• 構文としては正しいが不正なコードは
Compile Error (Fatal error)
ex: const A = 1 + f();
Opcode (vld)
line #* E I O op fetch ext return operands
----------------------------------------------------------------
2 0 E > RECV !0
3 1 NOP
2 FAST_CONCAT ~1 'HELLO+', !0
3 ECHO ~1
4 4 > RETURN null
line #* E I O op fetch ext return operands
----------------------------------------------------------------
2 0 E > NOP
6 1 INIT_FCALL 'hello'
2 SEND_VAL 'php'
3 DO_FCALL 0
4 > RETURN 1
function
hello()
call
hello()
この章のまとめ
• php はコンパイラを持っている
• 基本的には php スクリプトを読み込む度
にコンパイルが行われる
• 字句解析、構文解析、Opcode生成
構文解析の結果 AST が生成される
第2章
AST導入によって
変わったこと
cf.コンパイルの流れ
字句解析 構文解析 Opcode生成
狭義のコンパイル
AST を生成
トークンに分解
php5 (1 pass / 151構文(5.6))
字句解析 + 構文解析 + Opcode生成
php7 (2 pass / 127構文(7.0))
字句解析+構文解析 Opcode生成
php5 (1 pass / 151構文(5.6))
字句解析 + 構文解析 + Opcode生成
php7 (2 pass / 127構文(7.0))
字句解析+構文解析 Opcode生成
最適化の余地
Opcode生成時の
最適化例
定数の畳み込み
$sec_in_day = 60 * 60 * 24;
$sec_in_day = 86400;
※実は OpCache でも行われている
class A { const HOGE = ‘hoge‘; }
echo A::HOGE;
echo ‘hoge‘;
コンパイル時点で定義済みの定数に対してのみ有効
(autoload より pre include のほうが効きやすい)
静的関数展開(定数化)
• 関数呼び出しコストの削減
• 定数畳み込みとの組み合わせも有効
ex: strlen(’hoge’) + 1 -> 5
strlen(’hoge’) -> 4
ord(’A’) -> 65 / 7.1~
chr(65) -> ‘A‘ / 7.1~
静的関数展開(call)
• defined_funcが定義済みの場合のみ展開
• コンパイル時点で定義済みの関数に対し
てのみ発生 (定数と同様)
function func() {}
call_user_func(’func’);
func();
実際には、ほぼ等価であるものの若干異なり、
EXT_FCALL_BEGIN / EXT_FCALL_END が発行されない
静的関数展開(cast) / 7.1~
boolval($var) -> (bool)$var
intval($var) -> (int)$var
floatval($var) -> (float)$var
doubleval($var) -> (float)$var
strval($var) -> (string)$var
静的関数展開(Opcode変換)
is_null/is_bool/is_long/is_int/is_i
nteger/is_float/is_double/is_real/i
s_string/is_array/is_object/is_reso
urce -> TYPE_CHECK Op
defined -> DEFINED Op
静的関数展開の無効化
• 静的関数展開は、 CG(compiler_options)
に ZEND_COMPILE_NO_BUILTINS をセット
することで無効にできる
• CG(compiler_options) に
ZEND_COMPILE_NO_BUILTINS ビットをセッ
トすることで静的関数展開を無効にできる
• 拡張ならば、 CG(compiler_options) を制
御可能
静的zval構築
INIT_ARRAY(1) -> temp
ADD_ARRAY_ELEMENT(2) -> temp
ADD_ARRAY_ELEMENT(3) -> temp
ASSIGN(temp->$a)
ASSIGN([1,2,3]->$a)
$a = [1,2,3];
~5.6
7.0~
静的ショートサーキット
• condition 部分で行っている変数参照や関
数呼び出しに関する Opcode が生成されな
くなる
• JMPZ および 実行されることがないブロッ
クのOpcode は(無駄に)生成されてしまう
if (1 || condition) -> if (true)
if (0 && condition) -> if (false)
print の echo 化
• ZEND_PRINT 廃止 -> ZEND_ECHO に統一
• echo も print も同じ Opcode に
• print の戻り値が利用される場合のみ、
それを常に 1 で置き換え
return print(’hello’); echo ’hello’;
return 1;
条件コンパイル
“assert() は PHP 7 で言語構造となり、”
(http://php.net/manual/ja/function.assert.php)
とあるが、構文解析においては関数呼び出しで、
引数部のコード(AST) を逆変換している
assert($v === 0);
[zend.assertions >= 0]
assert(‘assert($v === 0)‘);
[zend.assertions < 0]
(assert の呼び出しがなったことに)
コンパイルタイミングによって
Opcode が変化する例
class A { const X = 1; }
a.php
require_once ‘a.php‘
echo A::X;
echo.php
require_once ‘a.php‘
require_once ‘echo.php‘
require.php
> php echo.php
echo.php をコンパイルする時点
では a.php はコンパイルされて
いない
> php require.php
echo.php をコンパイルする時点
で a.php はコンパイル済み
line #* E I O op fetch ext return operands
-----------------------------------------------------------
2 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE
3 1 FETCH_CONSTANT ~1 'A', 'X'
2 ECHO ~1
3 > RETURN 1
line #* E I O op fetch ext return operands
-----------------------------------------------------------
0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE
3 1 ECHO 1
2 > RETURN 1
> php echo.php
> php require.php (の時の echo.php)
あらかじめ require しておくことで早くなる可能性も……?(未検証)
line #* E I O op fetch ext return operands
-----------------------------------------------------------
2 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE
3 1 FETCH_CONSTANT ~1 'A', 'X'
2 ECHO ~1
3 > RETURN 1
line #* E I O op fetch ext return operands
-----------------------------------------------------------
0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE
3 1 ECHO 1
2 > RETURN 1
> php echo.php
> php require.php (の時の echo.php)
この章のまとめ
• AST 導入そのものによる影響は少ない
• コンパイルに関するコードがシンプルに
なり、最適化の余地が生まれた
• 小手先の最適化が不要になった
第3章 AST の
構造と特徴
Syntax tree(Parse tree)
構文木(解析木)
ex: 1 / (2 + 3)
1 2 3/ ( )+
解析木 :=トークンを葉として、
構成を木構造で表現したもの
Abstract syntax tree
抽象構文木
ex: 1 / (2 + 3)
1
2 3
+
/
抽象構文木 := 構文木から、
その後の処理に不要なデータ
をそぎ落としたもの
PHP の
抽象構文木
<?php
1/(2+3);
zend_ast (基本形)
• Zend/zend_ast.h / Zend/zend_ast.c
typedef uint16_t zend_ast_kind;
typedef uint16_t zend_ast_attr;
struct _zend_ast {
zend_ast_kind kind;
/* Type of the node (ZEND_AST_* enum constant) */
zend_ast_attr attr;
/* Additional attribute, use depending on node type */
uint32_t lineno;
/* Line number */
zend_ast *child[1];
/* Array of children (using struct hack) */
};
typedef struct _zend_ast zend_ast; // <- Zend/zend_types.h
Zend/zend_ast.h より 一部見やすさのために改変
zend_ast (基本形)
• Zend/zend_ast.h / Zend/zend_ast.c
typedef uint16_t zend_ast_kind;
typedef uint16_t zend_ast_attr;
struct _zend_ast {
zend_ast_kind kind;
/* Type of the node (ZEND_AST_* enum constant) */
zend_ast_attr attr;
/* Additional attribute, use depending on node type */
uint32_t lineno;
/* Line number */
zend_ast *child[1];
/* Array of children (using struct hack) */
};
typedef struct _zend_ast zend_ast; // <- Zend/zend_types.h
Zend/zend_ast.h より 一部見やすさのために改変
種別
行番号
子ノード
付属情報
PHP の
抽象構文木
<?php
1/(2+3);
種別
付属情報
子ノード
子ノード
zend_ast_kind
• ZEND_AST_*
• 全98種 (7.0) / 7.1は97種
• 大まかに分類して4系統
– 特殊ノード ZEND_AST_ZVAL / (ZEND_AST_ZNODE)
– 定義ノード ZEND_AST_CLASS など
– リストノード ZEND_AST_STMT_LIST など
– 通常ノード ZEND_AST_VAR など
ZEND_AST_ZVAL (特殊ノード)
• zval を包含するノード(行はzval に)
• zval := php スクリプトにおける変数
• リテラル や 変数名、呼び出し関数名等
• 常にリーフ(末端)
• zend_ast_create_zval / zend_ast_create_zval /
zend_ast_create_zval_from_str /
zend_ast_create_zval_from_long によって作成
• (余談) astを保持する zval(定数式)もある
typedef struct _zend_ast_zval {
zend_ast_kind kind;
zend_ast_attr attr;
zval val; /* Lineno is stored in val.u2.lineno */
} zend_ast_zval;
定義ノード
• ZEND_AST_FUNC_DECL / ZEND_AST_CLOSURE /
ZEND_AST_METHOD / ZEND_AST_CLASS のみ
• 常に4つ分の子要素を確保(NULL の場合も)
• zend_ast_create_decl によって作成
typedef struct _zend_ast_decl {
zend_ast_kind kind;
zend_ast_attr attr; /* Unused */
uint32_t start_lineno;
uint32_t end_lineno;
uint32_t flags;
unsigned char *lex_pos;
zend_string *doc_comment;
zend_string *name;
zend_ast *child[4];
} zend_ast_decl;
定義ノード
• AST_FUNC_DECL 関数定義
– 1:AST_PARAM_LIST(仮引数), 2:未使用,
3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型)
• AST_CLOSURE 無名関数定義
– 1:AST_PARAM_LIST(仮引数), 2:AST_CLOSURE_USES (use),
3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型)
• AST_METHOD メソッド定義
– 1:AST_PARAM_LIST(仮引数), 2:未使用,
3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型)
• AST_CLASS クラス,無名クラス,trait,interface 定義
– 1:AST_ZVAL(継承元), 2:AST_NAME_LIST (implements),
3:AST_STMT_LIST (内部), 4:未使用
リストノード
• 可変長の子を持つノード
• zend_ast_create_list によって作成 /
zend_ast_list_add で子を追加
• ex) ZEND_AST_STMT
– ZEND_AST_STMTは乱暴に言えば、ほぼ行。
– 子に ZEND_AST_STMT を含む場合もある
typedef struct _zend_ast_list {
zend_ast_kind kind;
zend_ast_attr attr;
uint32_t lineno;
uint32_t children;
zend_ast *child[1];
} zend_ast_list;
子の数
リストノード
• AST_ARG_LIST
– 関数、メソッド呼び出しの引数群
• AST_LIST (7.0まで)
–
• AST_ARRAY
– array 定義(全体/個々の要素は
AST_ARRAY_ELEM)
• AST_ENCAPS_LIST
– 変数を包含する文字列 (ダブルクォテーション文
字列、HEREDOC、バッククォテーション文字列)
• AST_EXPR_LIST
– for文の (x;x;x)
• AST_STMT_LIST
– ステートメント (; で終わる行すべて)
• AST_IF
– if 文全体 (子は AST_IF_ELEM)
• AST_SWITCH_LIST
– switch 文全体
• AST_CATCH_LIST
– catch 節 (子は AST_CATCH)
• AST_PARAM_LIST
– 関数、メソッド定義の引数群 (子は AST_PARAM)
• AST_CLOSURE_USES
– 無名関数の use 変数リスト (子は AST_ZVAL)
• AST_PROP_DECL
– プロパティ定義 (子は AST_PROP_ELEM)
• AST_CONST_DECL
– クラス外で定義される定数 (子は
AST_CONST_ELEM)
• AST_CLASS_CONST_DECL
– クラス外で定義される定数(const) (子は
AST_CONST_ELEM)
• AST_NAME_LIST
– interface の extends や insteadof の後に
続くクラス名群, catch のクラス名群(7.1) (子
は AST_ZVAL)
• AST_TRAIT_ADAPTATIONS
– trait の use のブレース内 (子は
AST_TRAIT_PRECEDENCE (insteadof) または
AST_TRAIT_ALIAS (as) )
• AST_USE
– 名前空間のuse (子は AST_USE_ELEM)
通常ノード
• 特殊,定義,リスト 以外のすべてのノード
• zend_ast_create (attr なし) / zend_ast_create_ex /
zend_ast_create_binary_op / zend_ast_create_assign_op /
zend_ast_create_cast によって作成
AST_VAR
AST_ZVAL
‘a‘
isset($a)
AST_ISSET
AST_ZVAL
‘func‘
AST_
ARG_LIST
func()
AST_CALL
リストノード
AST_ZVAL
‘A‘
AST_ZVAL
‘X‘
echo A::X
AST_CLASS
_CONST
AST_ECHO
通常ノード(1)
/* 0 child nodes */
• AST_MAGIC_CONST
– __LINE__, __FILE__ 等のマジック定数 / attr:=マ
ジック定数種別
• AST_TYPE
– 引数型指定 / attr=T_ARRAY,T_CALLABLE
/* 1 child node */
• AST_VAR
– 変数参照 / attr 未使用
• AST_CONST
– 定義済み定数(null,true,false,NAN などが子の
ZVALの値) / attr 未使用
• AST_UNPACK
– 関数呼び出し時の ...expr
• AST_UNARY_PLUS
– +expr / attr 未使用
• AST_UNARY_MINUS
– -expr / attr 未使用
• AST_CAST
– (int) とか./ attr := IS_LONG, IS_DOUBLE,
IS_STRING, IS_ARRAY, IS_OBJECT, _IS_BOOL,
IS_NULL
• AST_EMPTY
– empty(expr) / attr 未使用
• AST_ISSET
– isset(expr) / attr 未使用
• AST_SILENCE
– @expr (エラー抑制) / attr 未使用
• AST_SHELL_EXEC
– `backticks_expr` / attr 未使用
• AST_CLONE
– clone expr / attr 未使用
• AST_EXIT
– exit(expr) / attr 未使用
• AST_PRINT
– print expr / attr 未使用
• AST_INCLUDE_OR_EVAL
– include,require,eval / attr := 1:ZEND_EVAL,
2:ZEND_INCLUDE, 4:ZEND_INCLUDE_ONCE,
8:ZEND_REQUIRE ,16:ZEND_REQUIRE_ONCE
• AST_UNARY_OP
– !expr または ~expr / attr := ZEND_BOOL_NOT,
ZEND_BW_NOT
• AST_PRE_INC
– ++variable / attr 未使用
• AST_PRE_DEC
– --variable / attr 未使用
• AST_POST_INC
– variable++ / attr 未使用
• AST_POST_DEC
– variable-- / attr 未使用
• AST_YIELD_FROM
– yield from expr / attr 未使用
通常ノード(2)
• AST_GLOBAL
– global simple_variable / attr 未使用
• AST_UNSET
– unset(variable) / attr 未使用
• AST_RETURN
– return expr / attr 未使用
• AST_LABEL
– LABEL: / attr 未使用
• AST_REF
– &variable / attr 未使用
• AST_HALT_COMPILER
– __halt_compiler() 子には
__COMPILER_HALT_OFFSET__ にセットされる値
/ attr 未使用
• AST_ECHO
– echo expr / attr 未使用
• AST_THROW
– throw expr / attr 未使用
• AST_GOTO
– goto LABEL / attr 未使用
• AST_BREAK
– break expr / attr 未使用
• AST_CONTINUE
– continue expr / attr 未使用
/* 2 child nodes */
• AST_DIM
– 配列要素の参照 variable[N],variable{N}, 1:
参照対象配列, 2:指定要素 / attr 未使用
• AST_PROP
– プロパティ参照 variable->variable,
variable->{expr}, 1:参照対象オブジェクト,
2:指定プロパティ / attr 未使用
• AST_STATIC_PROP
– 静的プロパティ参照 variable::variable, 1:
参照対象クラス指定, 2:指定プロパティ / attr
未使用
• AST_CALL
– 関数呼び出し LABEL(), variable(), 1:呼び出
し関数指定, 2: AST_ARG_LIST / attr 未使用
• AST_CLASS_CONST
– クラス定数参照 class::const, 1:参照対象クラ
ス指定, 2:定数指定
• AST_ASSIGN
– 代入
• AST_ASSIGN_REF
– 参照代入
• AST_ASSIGN_OP
– 演算代入
• AST_BINARY_OP
– 四則演算
通常ノード(3)
• AST_GREATER
– >
• AST_GREATER_EQUAL
– >=
• AST_AND
– &&
• AST_OR
– ||
• AST_ARRAY_ELEM
– 配列リテラルの要素 1:value または key 2:key /
attr 0:通常 1:リファレンス
• AST_NEW
– new
• AST_INSTANCEOF
– instanceof
• AST_YIELD
– yield
• AST_COALESCE
– ??
• AST_STATIC
– 静的変数(非クラス)
• AST_WHILE
– while
• AST_DO_WHILE
– do-while
• AST_IF_ELEM
– if および elseif の条件(0)と ブロック(1) /
else は 条件なしでブロックのみ
• AST_SWITCH
– switch
• AST_SWITCH_CASE
– case
• AST_DECLARE
– declare
• AST_USE_TRAIT
– use (クラス内)
• AST_TRAIT_PRECEDENCE
– 0:AST_METHOD_REFERENCE(instead 指定の左側選ば
れるほう) 1:AST_NAME_LIST(instead 指定の右側ク
ラス名群)
• AST_METHOD_REFERENCE
– 0:AST_ZVAL(クラス名) 1:AST_ZVAL(メソッド名)
• AST_NAMESPACE
– namespace
• AST_USE_ELEM
– 0:AST_ZVAL(use で指定された本体) 1:AST_ZVAL(AS
以降)|NULL
• AST_TRAIT_ALIAS
– 0:AST_METHOD_REFERENCE
• AST_GROUP_USE
– グループ化されたuse (use A{B,C AS D})
0:AST_ZVAL(ブレース前) 1:AST_USE(ブレース内)
通常ノード(4)
/* 3 child nodes */
• AST_METHOD_CALL
– メソッド呼び出し
• AST_STATIC_CALL
– 静的メソッド呼び出し
• AST_CONDITIONAL
– 三項演算子 および ?:
• AST_TRY
– try 0:AST_STMT_LIST(try ブロック)
1:AST_CATCH_LIST
2:AST_STMT_LIST(finally ブロッ
ク)|NULL
• AST_CATCH
– catch 0:AST_ZVAL(catch するクラス
名) 1:AST_ZVAL(変数名)
2:AST_STMT_LIST(catch ブロック) /
7.1 になって、 0 は AST_NAME_LIST
に変更 (複数指定できるようになったの
で)
• AST_PARAM
– 引数定義 0:AST_ZVAL(型)|NULL
1:AST_ZVAL(変数名) 2:AST_ZVAL(デ
フォルト値)|NULL ref_flag?
• AST_PROP_ELEM
– プロパティ定義 0:AST_ZVAL(プロパティ
名) 1:expr(値)
2:AST_ZVAL(doc_comment)
• AST_CONST_ELEM
– 定数定義 0:AST_ZVAL(定数名)
2:expr(定数値)
/* 4 child nodes */
• AST_FOR
– for(0;1;2) {3}
• AST_FOREACH
– foreach(0 as 1=>2){3} あるいは
foreach(0 as 1){3} (2未使用)
コードとASTの対比
function hello($name){
echo "Hello $name";
}
hello('php');
型 デフォルト引数
戻り値の型
未使用
HHVM における AST
• AST ノードの基底クラスである
HPHP::Construct があり、Statement と
Expression に分かれる
• HPHP::Compiler::Parser::parseImpl が、
parseImpl7 あるいは parseImpl5 を呼び
出し、HPHP::Compiler::Parser::m_tree
に StatementList が作られる
• zend_ast_kind のそれぞれに対応するクラ
スがある感じ
HPHP::Statement
• 構造を表すノードの
基本クラス
• HPHP::StatementList が
ZEND_STATEMENT_LIST に
相当
HPHP::Expression
• 評価式や値を表す
ノードの基本クラス
• AwaitExpression
あたりは hhvm なら
では
PHP AST の特徴
構文が変化すれば構造が変わる
• Short List Syntax
– (ZEND_)AST_LIST 廃止
– AST_ARRAY: attr に array 形式を保持
• Class Constant Visibility
– AST_CONST_DECL: attr にアクセス権を保持
– AST_CONST_ELEM: 2child から 3childに
• Catching Multiple Exception
– catch (E $e) -> catch (E1|E2 $e)
– AST_CATCH の 1番目の子要素が ZVAL 単体だったが、
7.1 で AST_NAME_LIST -> AST_ZVAL に
ZEND_ARRAY_SYNTAX_LIST (list)
ZEND_ARRAY_SYNTAX_LONG (array)
ZEND_ARRAY_SYNTAX_SHORT ([])
逆変換可能
• 元のコードと等価なコードを作れる
– 括弧やインデントは最低限必要なものだけ
• zend_ast_export 関数
– assert に利用されている
– 後述する Astkit::export で利用可能
• 自前で用意すれば別のコードにもできる
専用のメモリ空間
• CG(ast_arena)
– zend_arena_createによって確保
– 初期サイズは 32MB / 必要に応じて拡張
• zend_ast_alloc 関数
– AST生成用のメモリを割り当てるための関数
– zend_arena_alloc を利用し、 CG(ast_arena) か
らメモリを割り当てる(足りなければ拡張する)
• Opcode生成後に解放
– zend_arena_destroyにて解放
短命
• parse時に作られてOpcodeを生成したら破棄
される
• 現存の拡張は、parse後にzval(phpから扱え
る変数)に変換することで php スクリプト
から利用可能にしている
• zend_ast_process 関数ポインタを使って
フック可能
– AST構築直後 (Opcode 生成前)に呼ばれる
– 拡張を書けば AST 改変も可能
この章のまとめ
• ASTノードの主な構成要素は種別、付属情報、
行番号、子ノード
• 4つに大別 特殊,定義,リスト,通常
• バージョン間での互換性は考えられていない
• 拡張からであればいろいろといじれる
– 現存する拡張だけではできないこともある
第4章
ASTの利用法
既存の拡張を利用する
php-ast
• https://github.com/nikic/php-ast
• astparse_file あるいは
astparse_code で AST 構築
• astNode をベースクラスとした astDecl
• リスト型のノード は Node に統合
• Zval型のノードは Node の exprプロパティ
• STMT_LIST(A) の子要素に STMT_LIST(B) が含
まれる場合は、B の子を A の子として併合
astkit
• https://github.com/sgolemon/astkit
• AstKit::parseString あるいは
AstKit::parseFile で AST構築
• AstKit をベースクラスとした AstKitList,
AstKitDecl, AstKitZval にマッピングさ
れる
• $AstKit->export でコードに変換
astparse_code('<?php 1 + 2;')
全ノードをphp スクリプトで扱える構造に変換 (CG(ast) は破棄)
C言語 (CG(ast))
array
astnode
kind: 520
flags: 1
lineno: 1
left: 1
right: 2
astNode
kind: 133
flags:0
lineno: 0
children:
AST_ZVAL
1
AST_ZVAL
2
AST_BINA
RY_OP +
AST_STMT
_LIST
ast_to_zval
php スクリプト (zval)
Astkit::parseString('1+2;')
先頭のノードのみ生成。操作により子の AstKit が生成される
C language (CG(ast) =
astkit_tree->tree)
AstKitList
AST_ZVAL
1
AST_ZVAL
2
AST_BINA
RY_OP +
AST_STMT
_LIST
php script (zval)
AstKit
AstKitZval
getChild(0) で生成
getChild(0,false) で生成
getChild(0) ならば int(1)
それぞれの特徴
• php-ast
– php スクリプトから扱いやすい
– 初期のコストが大きめ
– 異なるバージョンでの変換処理を拡張側で頑張っ
てる部分もある
• astkit
– C の ast そのままのメモリを操作
– 利用する箇所が部分的ならば低コストか
– ast 構造の変化によって php 側での操作が大き
く変わる
利用例
• https://github.com/etsy/phan
• php-ast を利用した静的解析ツール
• 詳細は別セッションでされてるんじゃな
いかな
新たに拡張を作る
phpast
• https://github.com/do-aki/phpast
• 勉強目的で作った
• ASTの可視化: phpast + graphviz
https://dooakitestapp.herokuapp.com/phpa
st/webapp/
• php のコードで ast を操作して、実行する予定
のコードを改変することができたら面白いよなぁ
という妄想をしつつ、いまだ妄想のまま
今後考えられる AST 利用例
• さらなる最適化
– まだまだ静的に解決できる個所はある
– この拡張を導入するだけで速くなる! なんてことも
• コンバータ(トランスパイラ) (php7 -> hack)
– php7 のコードから型推論できれば、あるいは
– cf: https://speakerdeck.com/anatoo/type-
inference-on-php
• syntax grep (一致する構文を探索,置換)
• power assert (実行過程をひとつひとつ出力)
AST導入により 今後考えられる
php の進化
• コード(AST)を受け取ることができる関数
– 今は assert のみ
– ユーザランドでこれができると面白い
– php のコードがそのままSQLになったり
• ソースコードフィルタ
– ルールに従って AST を入れ替える
– 難読化に使える?
※ただの妄想です
まとめ
• ASTそのものは複雑なものではない
• 拡張を使ってASTを操作することもできる
けど、拡張を書けばもっといろいろでき
る
• 興味を持ったら拡張書いてみよう!
以上
• もっと深く知りたい人は闇php勉強会へ
(blank)

Contenu connexe

Tendances

Zend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るZend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るYoshio Hanawa
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐりKazuyuki TAKASE
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーyoku0825
 
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―shinjiigarashi
 
文字コードに起因する脆弱性とその対策
文字コードに起因する脆弱性とその対策 文字コードに起因する脆弱性とその対策
文字コードに起因する脆弱性とその対策 Hiroshi Tokumaru
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールMITSUNARI Shigeo
 
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみたOPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみたYoshio Hanawa
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンAkihiko Horiuchi
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
PSR-1 と PSR-2 を 5分でざっくり理解する
PSR-1 と PSR-2 を5分でざっくり理解するPSR-1 と PSR-2 を5分でざっくり理解する
PSR-1 と PSR-2 を 5分でざっくり理解するWataru Terada
 
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜Teppei Sato
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミングPreferred Networks
 
CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討Masahito Zembutsu
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!kwatch
 
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。sasezaki
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについてkumake
 
わかった気になるMySQL
わかった気になるMySQLわかった気になるMySQL
わかった気になるMySQLyoku0825
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safeKumazaki Hiroki
 

Tendances (20)

Zend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るZend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探る
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
 
文字コードに起因する脆弱性とその対策
文字コードに起因する脆弱性とその対策 文字コードに起因する脆弱性とその対策
文字コードに起因する脆弱性とその対策
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
 
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみたOPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
PSR-1 と PSR-2 を 5分でざっくり理解する
PSR-1 と PSR-2 を5分でざっくり理解するPSR-1 と PSR-2 を5分でざっくり理解する
PSR-1 と PSR-2 を 5分でざっくり理解する
 
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミング
 
CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
 
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。
 
WebSocket / WebRTCの技術紹介
WebSocket / WebRTCの技術紹介WebSocket / WebRTCの技術紹介
WebSocket / WebRTCの技術紹介
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
 
わかった気になるMySQL
わかった気になるMySQLわかった気になるMySQL
わかった気になるMySQL
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safe
 

Similaire à PHP AST 徹底解説

php7's ast
php7's astphp7's ast
php7's astdo_aki
 
Php in ruby
Php in rubyPhp in ruby
Php in rubydo_aki
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
textsearch_jaで全文検索
textsearch_jaで全文検索textsearch_jaで全文検索
textsearch_jaで全文検索Akio Ishida
 
詳説ぺちぺち
詳説ぺちぺち詳説ぺちぺち
詳説ぺちぺちdo_aki
 
Buffer overflow
Buffer overflowBuffer overflow
Buffer overflowionis111
 
GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法博文 斉藤
 
モダンmod_perl入門 #yapcasia
モダンmod_perl入門 #yapcasiaモダンmod_perl入門 #yapcasia
モダンmod_perl入門 #yapcasia鉄次 尾形
 
ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)taskie
 
x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチMasami Ichikawa
 
知って得するC#
知って得するC#知って得するC#
知って得するC#Shota Baba
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPAkira Takahashi
 
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesasm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesNoritada Shimizu
 
Scala2.8への移行
Scala2.8への移行Scala2.8への移行
Scala2.8への移行guest5f4320
 
メタメタプログラミングRuby
メタメタプログラミングRubyメタメタプログラミングRuby
メタメタプログラミングRubyemasaka
 

Similaire à PHP AST 徹底解説 (20)

php7's ast
php7's astphp7's ast
php7's ast
 
Php in ruby
Php in rubyPhp in ruby
Php in ruby
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
What is Metasepi?
What is Metasepi?What is Metasepi?
What is Metasepi?
 
textsearch_jaで全文検索
textsearch_jaで全文検索textsearch_jaで全文検索
textsearch_jaで全文検索
 
詳説ぺちぺち
詳説ぺちぺち詳説ぺちぺち
詳説ぺちぺち
 
Buffer overflow
Buffer overflowBuffer overflow
Buffer overflow
 
GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法GNU awk (gawk) を用いた Apache ログ解析方法
GNU awk (gawk) を用いた Apache ログ解析方法
 
Boost Fusion Library
Boost Fusion LibraryBoost Fusion Library
Boost Fusion Library
 
モダンmod_perl入門 #yapcasia
モダンmod_perl入門 #yapcasiaモダンmod_perl入門 #yapcasia
モダンmod_perl入門 #yapcasia
 
ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)
 
x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
 
知って得するC#
知って得するC#知って得するC#
知って得するC#
 
Perl勉強会#2資料
Perl勉強会#2資料Perl勉強会#2資料
Perl勉強会#2資料
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JP
 
asm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web gamesasm.js x emscripten: The foundation of the next level Web games
asm.js x emscripten: The foundation of the next level Web games
 
Scala2.8への移行
Scala2.8への移行Scala2.8への移行
Scala2.8への移行
 
Scala2.8への移行
Scala2.8への移行Scala2.8への移行
Scala2.8への移行
 
メタメタプログラミングRuby
メタメタプログラミングRubyメタメタプログラミングRuby
メタメタプログラミングRuby
 
Haskell で CLI
Haskell で CLIHaskell で CLI
Haskell で CLI
 

Plus de do_aki

Tritonn から Elasticsearch への移行話
Tritonn から Elasticsearch への移行話Tritonn から Elasticsearch への移行話
Tritonn から Elasticsearch への移行話do_aki
 
PHPとシグナル、その裏側
PHPとシグナル、その裏側PHPとシグナル、その裏側
PHPとシグナル、その裏側do_aki
 
再考:列挙型
再考:列挙型再考:列挙型
再考:列挙型do_aki
 
signal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かsignal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かdo_aki
 
PHP AST 徹底解説(補遺)
PHP AST 徹底解説(補遺)PHP AST 徹底解説(補遺)
PHP AST 徹底解説(補遺)do_aki
 
Writing php extensions in golang
Writing php extensions in golangWriting php extensions in golang
Writing php extensions in golangdo_aki
 
N対1 レプリケーション + Optimizer Hint
N対1 レプリケーション + Optimizer HintN対1 レプリケーション + Optimizer Hint
N対1 レプリケーション + Optimizer Hintdo_aki
 
20150212 プレゼンテーションzen
20150212 プレゼンテーションzen20150212 プレゼンテーションzen
20150212 プレゼンテーションzendo_aki
 
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」do_aki
 
20141017 introduce razor
20141017 introduce razor20141017 introduce razor
20141017 introduce razordo_aki
 
20141011 mastering mysqlnd
20141011 mastering mysqlnd20141011 mastering mysqlnd
20141011 mastering mysqlnddo_aki
 
php in ruby
php in rubyphp in ruby
php in rubydo_aki
 
PHP から Groonga を使うにはこんなコードになるよ!
PHP から Groonga を使うにはこんなコードになるよ!PHP から Groonga を使うにはこんなコードになるよ!
PHP から Groonga を使うにはこんなコードになるよ!do_aki
 
N:1 Replication meets MHA
N:1 Replication meets MHAN:1 Replication meets MHA
N:1 Replication meets MHAdo_aki
 
Php radomize
Php radomizePhp radomize
Php radomizedo_aki
 
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editorセキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editordo_aki
 
マスタN対スレーブ1レプリケーションの作り方 ~あれから~
マスタN対スレーブ1レプリケーションの作り方 ~あれから~マスタN対スレーブ1レプリケーションの作り方 ~あれから~
マスタN対スレーブ1レプリケーションの作り方 ~あれから~do_aki
 
Immortal
ImmortalImmortal
Immortaldo_aki
 
Excel is image viewer
Excel is image viewerExcel is image viewer
Excel is image viewerdo_aki
 
A bridge between php and ruby
A bridge between php and ruby A bridge between php and ruby
A bridge between php and ruby do_aki
 

Plus de do_aki (20)

Tritonn から Elasticsearch への移行話
Tritonn から Elasticsearch への移行話Tritonn から Elasticsearch への移行話
Tritonn から Elasticsearch への移行話
 
PHPとシグナル、その裏側
PHPとシグナル、その裏側PHPとシグナル、その裏側
PHPとシグナル、その裏側
 
再考:列挙型
再考:列挙型再考:列挙型
再考:列挙型
 
signal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かsignal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何か
 
PHP AST 徹底解説(補遺)
PHP AST 徹底解説(補遺)PHP AST 徹底解説(補遺)
PHP AST 徹底解説(補遺)
 
Writing php extensions in golang
Writing php extensions in golangWriting php extensions in golang
Writing php extensions in golang
 
N対1 レプリケーション + Optimizer Hint
N対1 レプリケーション + Optimizer HintN対1 レプリケーション + Optimizer Hint
N対1 レプリケーション + Optimizer Hint
 
20150212 プレゼンテーションzen
20150212 プレゼンテーションzen20150212 プレゼンテーションzen
20150212 プレゼンテーションzen
 
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
 
20141017 introduce razor
20141017 introduce razor20141017 introduce razor
20141017 introduce razor
 
20141011 mastering mysqlnd
20141011 mastering mysqlnd20141011 mastering mysqlnd
20141011 mastering mysqlnd
 
php in ruby
php in rubyphp in ruby
php in ruby
 
PHP から Groonga を使うにはこんなコードになるよ!
PHP から Groonga を使うにはこんなコードになるよ!PHP から Groonga を使うにはこんなコードになるよ!
PHP から Groonga を使うにはこんなコードになるよ!
 
N:1 Replication meets MHA
N:1 Replication meets MHAN:1 Replication meets MHA
N:1 Replication meets MHA
 
Php radomize
Php radomizePhp radomize
Php radomize
 
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editorセキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
 
マスタN対スレーブ1レプリケーションの作り方 ~あれから~
マスタN対スレーブ1レプリケーションの作り方 ~あれから~マスタN対スレーブ1レプリケーションの作り方 ~あれから~
マスタN対スレーブ1レプリケーションの作り方 ~あれから~
 
Immortal
ImmortalImmortal
Immortal
 
Excel is image viewer
Excel is image viewerExcel is image viewer
Excel is image viewer
 
A bridge between php and ruby
A bridge between php and ruby A bridge between php and ruby
A bridge between php and ruby
 

Dernier

デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 

Dernier (8)

デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 

PHP AST 徹底解説

  • 1. 2016/11/03 PHP Conference Japan 2016 do_aki 1 updated 2016-12-13
  • 3.
  • 5. PHP Compiler in PHP PHP Script Opcode Request Output Compiler Lexing Parsing Compilation VM Execution INCLUDE_OR_EVAL
  • 7. Lexical Analysis 字句解析 • ソースコードをトークン(意味を持つ最 小の単位)に分解 • token_get_all 関数 で確認できる • 余談: token_get_all の中では実際にコ ンパイル処理が行われる(ただし、 Opcode 生成は省略)
  • 8. ソースコード(php スクリプト) <?php function hello ( $name ) { echo “HELLO $name“ ; } hello ( “php“ ) ;
  • 9. 字句解析 <?php function hello ( $name ) { echo “HELLO $name“ ; } hello ( “php“ ) ; T_FUNCTION T_STRING ( ) { } T_ECHO T_ENCAPSED_AND _WHITESPACE ; T_STRING ( ) ; T_OPEN_TAG T_VARIABLE T_VARIABLE T_VARIABLE“ “ ひとつひとつがトークン (意味を持つ最小の単位)
  • 10. Syntax Analysis 構文解析 • トークンの並びから構文を導く • 構文に応じたASTを構築する • 該当する構文が見つからないときは Parse Error (7.0 から Exception)
  • 11. 構文解析 T_FUNCTION T_STRING ( ) { } T_ECHO T_ENCAPSED_AND_WHITESPACE ; T_STRING ( ) ; T_OPEN_TAG T_VARIABLE T_VARIABLE function declaration function call
  • 13. Bytecode Generation Opcode生成 • AST を解析して Opcode を生成 • 狭義のコンパイル(zend_compile.c) • いくつかの最適化が施される(後述) • 構文としては正しいが不正なコードは Compile Error (Fatal error) ex: const A = 1 + f();
  • 14. Opcode (vld) line #* E I O op fetch ext return operands ---------------------------------------------------------------- 2 0 E > RECV !0 3 1 NOP 2 FAST_CONCAT ~1 'HELLO+', !0 3 ECHO ~1 4 4 > RETURN null line #* E I O op fetch ext return operands ---------------------------------------------------------------- 2 0 E > NOP 6 1 INIT_FCALL 'hello' 2 SEND_VAL 'php' 3 DO_FCALL 0 4 > RETURN 1 function hello() call hello()
  • 15. この章のまとめ • php はコンパイラを持っている • 基本的には php スクリプトを読み込む度 にコンパイルが行われる • 字句解析、構文解析、Opcode生成 構文解析の結果 AST が生成される
  • 18. php5 (1 pass / 151構文(5.6)) 字句解析 + 構文解析 + Opcode生成 php7 (2 pass / 127構文(7.0)) 字句解析+構文解析 Opcode生成
  • 19. php5 (1 pass / 151構文(5.6)) 字句解析 + 構文解析 + Opcode生成 php7 (2 pass / 127構文(7.0)) 字句解析+構文解析 Opcode生成 最適化の余地
  • 21. 定数の畳み込み $sec_in_day = 60 * 60 * 24; $sec_in_day = 86400; ※実は OpCache でも行われている class A { const HOGE = ‘hoge‘; } echo A::HOGE; echo ‘hoge‘; コンパイル時点で定義済みの定数に対してのみ有効 (autoload より pre include のほうが効きやすい)
  • 22. 静的関数展開(定数化) • 関数呼び出しコストの削減 • 定数畳み込みとの組み合わせも有効 ex: strlen(’hoge’) + 1 -> 5 strlen(’hoge’) -> 4 ord(’A’) -> 65 / 7.1~ chr(65) -> ‘A‘ / 7.1~
  • 23. 静的関数展開(call) • defined_funcが定義済みの場合のみ展開 • コンパイル時点で定義済みの関数に対し てのみ発生 (定数と同様) function func() {} call_user_func(’func’); func(); 実際には、ほぼ等価であるものの若干異なり、 EXT_FCALL_BEGIN / EXT_FCALL_END が発行されない
  • 24. 静的関数展開(cast) / 7.1~ boolval($var) -> (bool)$var intval($var) -> (int)$var floatval($var) -> (float)$var doubleval($var) -> (float)$var strval($var) -> (string)$var
  • 26. 静的関数展開の無効化 • 静的関数展開は、 CG(compiler_options) に ZEND_COMPILE_NO_BUILTINS をセット することで無効にできる • CG(compiler_options) に ZEND_COMPILE_NO_BUILTINS ビットをセッ トすることで静的関数展開を無効にできる • 拡張ならば、 CG(compiler_options) を制 御可能
  • 27. 静的zval構築 INIT_ARRAY(1) -> temp ADD_ARRAY_ELEMENT(2) -> temp ADD_ARRAY_ELEMENT(3) -> temp ASSIGN(temp->$a) ASSIGN([1,2,3]->$a) $a = [1,2,3]; ~5.6 7.0~
  • 28. 静的ショートサーキット • condition 部分で行っている変数参照や関 数呼び出しに関する Opcode が生成されな くなる • JMPZ および 実行されることがないブロッ クのOpcode は(無駄に)生成されてしまう if (1 || condition) -> if (true) if (0 && condition) -> if (false)
  • 29. print の echo 化 • ZEND_PRINT 廃止 -> ZEND_ECHO に統一 • echo も print も同じ Opcode に • print の戻り値が利用される場合のみ、 それを常に 1 で置き換え return print(’hello’); echo ’hello’; return 1;
  • 30. 条件コンパイル “assert() は PHP 7 で言語構造となり、” (http://php.net/manual/ja/function.assert.php) とあるが、構文解析においては関数呼び出しで、 引数部のコード(AST) を逆変換している assert($v === 0); [zend.assertions >= 0] assert(‘assert($v === 0)‘); [zend.assertions < 0] (assert の呼び出しがなったことに)
  • 31. コンパイルタイミングによって Opcode が変化する例 class A { const X = 1; } a.php require_once ‘a.php‘ echo A::X; echo.php require_once ‘a.php‘ require_once ‘echo.php‘ require.php > php echo.php echo.php をコンパイルする時点 では a.php はコンパイルされて いない > php require.php echo.php をコンパイルする時点 で a.php はコンパイル済み
  • 32. line #* E I O op fetch ext return operands ----------------------------------------------------------- 2 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE 3 1 FETCH_CONSTANT ~1 'A', 'X' 2 ECHO ~1 3 > RETURN 1 line #* E I O op fetch ext return operands ----------------------------------------------------------- 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE 3 1 ECHO 1 2 > RETURN 1 > php echo.php > php require.php (の時の echo.php)
  • 33. あらかじめ require しておくことで早くなる可能性も……?(未検証) line #* E I O op fetch ext return operands ----------------------------------------------------------- 2 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE 3 1 FETCH_CONSTANT ~1 'A', 'X' 2 ECHO ~1 3 > RETURN 1 line #* E I O op fetch ext return operands ----------------------------------------------------------- 0 E > INCLUDE_OR_EVAL 'a.php', REQUIRE_ONCE 3 1 ECHO 1 2 > RETURN 1 > php echo.php > php require.php (の時の echo.php)
  • 34. この章のまとめ • AST 導入そのものによる影響は少ない • コンパイルに関するコードがシンプルに なり、最適化の余地が生まれた • 小手先の最適化が不要になった
  • 36. Syntax tree(Parse tree) 構文木(解析木) ex: 1 / (2 + 3) 1 2 3/ ( )+ 解析木 :=トークンを葉として、 構成を木構造で表現したもの
  • 37. Abstract syntax tree 抽象構文木 ex: 1 / (2 + 3) 1 2 3 + / 抽象構文木 := 構文木から、 その後の処理に不要なデータ をそぎ落としたもの
  • 39. zend_ast (基本形) • Zend/zend_ast.h / Zend/zend_ast.c typedef uint16_t zend_ast_kind; typedef uint16_t zend_ast_attr; struct _zend_ast { zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */ zend_ast_attr attr; /* Additional attribute, use depending on node type */ uint32_t lineno; /* Line number */ zend_ast *child[1]; /* Array of children (using struct hack) */ }; typedef struct _zend_ast zend_ast; // <- Zend/zend_types.h Zend/zend_ast.h より 一部見やすさのために改変
  • 40. zend_ast (基本形) • Zend/zend_ast.h / Zend/zend_ast.c typedef uint16_t zend_ast_kind; typedef uint16_t zend_ast_attr; struct _zend_ast { zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */ zend_ast_attr attr; /* Additional attribute, use depending on node type */ uint32_t lineno; /* Line number */ zend_ast *child[1]; /* Array of children (using struct hack) */ }; typedef struct _zend_ast zend_ast; // <- Zend/zend_types.h Zend/zend_ast.h より 一部見やすさのために改変 種別 行番号 子ノード 付属情報
  • 42. zend_ast_kind • ZEND_AST_* • 全98種 (7.0) / 7.1は97種 • 大まかに分類して4系統 – 特殊ノード ZEND_AST_ZVAL / (ZEND_AST_ZNODE) – 定義ノード ZEND_AST_CLASS など – リストノード ZEND_AST_STMT_LIST など – 通常ノード ZEND_AST_VAR など
  • 43. ZEND_AST_ZVAL (特殊ノード) • zval を包含するノード(行はzval に) • zval := php スクリプトにおける変数 • リテラル や 変数名、呼び出し関数名等 • 常にリーフ(末端) • zend_ast_create_zval / zend_ast_create_zval / zend_ast_create_zval_from_str / zend_ast_create_zval_from_long によって作成 • (余談) astを保持する zval(定数式)もある typedef struct _zend_ast_zval { zend_ast_kind kind; zend_ast_attr attr; zval val; /* Lineno is stored in val.u2.lineno */ } zend_ast_zval;
  • 44. 定義ノード • ZEND_AST_FUNC_DECL / ZEND_AST_CLOSURE / ZEND_AST_METHOD / ZEND_AST_CLASS のみ • 常に4つ分の子要素を確保(NULL の場合も) • zend_ast_create_decl によって作成 typedef struct _zend_ast_decl { zend_ast_kind kind; zend_ast_attr attr; /* Unused */ uint32_t start_lineno; uint32_t end_lineno; uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; zend_ast *child[4]; } zend_ast_decl;
  • 45. 定義ノード • AST_FUNC_DECL 関数定義 – 1:AST_PARAM_LIST(仮引数), 2:未使用, 3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型) • AST_CLOSURE 無名関数定義 – 1:AST_PARAM_LIST(仮引数), 2:AST_CLOSURE_USES (use), 3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型) • AST_METHOD メソッド定義 – 1:AST_PARAM_LIST(仮引数), 2:未使用, 3:AST_STMT_LIST (内部), 4: AST_ZVAL(戻り値型) • AST_CLASS クラス,無名クラス,trait,interface 定義 – 1:AST_ZVAL(継承元), 2:AST_NAME_LIST (implements), 3:AST_STMT_LIST (内部), 4:未使用
  • 46. リストノード • 可変長の子を持つノード • zend_ast_create_list によって作成 / zend_ast_list_add で子を追加 • ex) ZEND_AST_STMT – ZEND_AST_STMTは乱暴に言えば、ほぼ行。 – 子に ZEND_AST_STMT を含む場合もある typedef struct _zend_ast_list { zend_ast_kind kind; zend_ast_attr attr; uint32_t lineno; uint32_t children; zend_ast *child[1]; } zend_ast_list; 子の数
  • 47. リストノード • AST_ARG_LIST – 関数、メソッド呼び出しの引数群 • AST_LIST (7.0まで) – • AST_ARRAY – array 定義(全体/個々の要素は AST_ARRAY_ELEM) • AST_ENCAPS_LIST – 変数を包含する文字列 (ダブルクォテーション文 字列、HEREDOC、バッククォテーション文字列) • AST_EXPR_LIST – for文の (x;x;x) • AST_STMT_LIST – ステートメント (; で終わる行すべて) • AST_IF – if 文全体 (子は AST_IF_ELEM) • AST_SWITCH_LIST – switch 文全体 • AST_CATCH_LIST – catch 節 (子は AST_CATCH) • AST_PARAM_LIST – 関数、メソッド定義の引数群 (子は AST_PARAM) • AST_CLOSURE_USES – 無名関数の use 変数リスト (子は AST_ZVAL) • AST_PROP_DECL – プロパティ定義 (子は AST_PROP_ELEM) • AST_CONST_DECL – クラス外で定義される定数 (子は AST_CONST_ELEM) • AST_CLASS_CONST_DECL – クラス外で定義される定数(const) (子は AST_CONST_ELEM) • AST_NAME_LIST – interface の extends や insteadof の後に 続くクラス名群, catch のクラス名群(7.1) (子 は AST_ZVAL) • AST_TRAIT_ADAPTATIONS – trait の use のブレース内 (子は AST_TRAIT_PRECEDENCE (insteadof) または AST_TRAIT_ALIAS (as) ) • AST_USE – 名前空間のuse (子は AST_USE_ELEM)
  • 48. 通常ノード • 特殊,定義,リスト 以外のすべてのノード • zend_ast_create (attr なし) / zend_ast_create_ex / zend_ast_create_binary_op / zend_ast_create_assign_op / zend_ast_create_cast によって作成 AST_VAR AST_ZVAL ‘a‘ isset($a) AST_ISSET AST_ZVAL ‘func‘ AST_ ARG_LIST func() AST_CALL リストノード AST_ZVAL ‘A‘ AST_ZVAL ‘X‘ echo A::X AST_CLASS _CONST AST_ECHO
  • 49. 通常ノード(1) /* 0 child nodes */ • AST_MAGIC_CONST – __LINE__, __FILE__ 等のマジック定数 / attr:=マ ジック定数種別 • AST_TYPE – 引数型指定 / attr=T_ARRAY,T_CALLABLE /* 1 child node */ • AST_VAR – 変数参照 / attr 未使用 • AST_CONST – 定義済み定数(null,true,false,NAN などが子の ZVALの値) / attr 未使用 • AST_UNPACK – 関数呼び出し時の ...expr • AST_UNARY_PLUS – +expr / attr 未使用 • AST_UNARY_MINUS – -expr / attr 未使用 • AST_CAST – (int) とか./ attr := IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, _IS_BOOL, IS_NULL • AST_EMPTY – empty(expr) / attr 未使用 • AST_ISSET – isset(expr) / attr 未使用 • AST_SILENCE – @expr (エラー抑制) / attr 未使用 • AST_SHELL_EXEC – `backticks_expr` / attr 未使用 • AST_CLONE – clone expr / attr 未使用 • AST_EXIT – exit(expr) / attr 未使用 • AST_PRINT – print expr / attr 未使用 • AST_INCLUDE_OR_EVAL – include,require,eval / attr := 1:ZEND_EVAL, 2:ZEND_INCLUDE, 4:ZEND_INCLUDE_ONCE, 8:ZEND_REQUIRE ,16:ZEND_REQUIRE_ONCE • AST_UNARY_OP – !expr または ~expr / attr := ZEND_BOOL_NOT, ZEND_BW_NOT • AST_PRE_INC – ++variable / attr 未使用 • AST_PRE_DEC – --variable / attr 未使用 • AST_POST_INC – variable++ / attr 未使用 • AST_POST_DEC – variable-- / attr 未使用 • AST_YIELD_FROM – yield from expr / attr 未使用
  • 50. 通常ノード(2) • AST_GLOBAL – global simple_variable / attr 未使用 • AST_UNSET – unset(variable) / attr 未使用 • AST_RETURN – return expr / attr 未使用 • AST_LABEL – LABEL: / attr 未使用 • AST_REF – &variable / attr 未使用 • AST_HALT_COMPILER – __halt_compiler() 子には __COMPILER_HALT_OFFSET__ にセットされる値 / attr 未使用 • AST_ECHO – echo expr / attr 未使用 • AST_THROW – throw expr / attr 未使用 • AST_GOTO – goto LABEL / attr 未使用 • AST_BREAK – break expr / attr 未使用 • AST_CONTINUE – continue expr / attr 未使用 /* 2 child nodes */ • AST_DIM – 配列要素の参照 variable[N],variable{N}, 1: 参照対象配列, 2:指定要素 / attr 未使用 • AST_PROP – プロパティ参照 variable->variable, variable->{expr}, 1:参照対象オブジェクト, 2:指定プロパティ / attr 未使用 • AST_STATIC_PROP – 静的プロパティ参照 variable::variable, 1: 参照対象クラス指定, 2:指定プロパティ / attr 未使用 • AST_CALL – 関数呼び出し LABEL(), variable(), 1:呼び出 し関数指定, 2: AST_ARG_LIST / attr 未使用 • AST_CLASS_CONST – クラス定数参照 class::const, 1:参照対象クラ ス指定, 2:定数指定 • AST_ASSIGN – 代入 • AST_ASSIGN_REF – 参照代入 • AST_ASSIGN_OP – 演算代入 • AST_BINARY_OP – 四則演算
  • 51. 通常ノード(3) • AST_GREATER – > • AST_GREATER_EQUAL – >= • AST_AND – && • AST_OR – || • AST_ARRAY_ELEM – 配列リテラルの要素 1:value または key 2:key / attr 0:通常 1:リファレンス • AST_NEW – new • AST_INSTANCEOF – instanceof • AST_YIELD – yield • AST_COALESCE – ?? • AST_STATIC – 静的変数(非クラス) • AST_WHILE – while • AST_DO_WHILE – do-while • AST_IF_ELEM – if および elseif の条件(0)と ブロック(1) / else は 条件なしでブロックのみ • AST_SWITCH – switch • AST_SWITCH_CASE – case • AST_DECLARE – declare • AST_USE_TRAIT – use (クラス内) • AST_TRAIT_PRECEDENCE – 0:AST_METHOD_REFERENCE(instead 指定の左側選ば れるほう) 1:AST_NAME_LIST(instead 指定の右側ク ラス名群) • AST_METHOD_REFERENCE – 0:AST_ZVAL(クラス名) 1:AST_ZVAL(メソッド名) • AST_NAMESPACE – namespace • AST_USE_ELEM – 0:AST_ZVAL(use で指定された本体) 1:AST_ZVAL(AS 以降)|NULL • AST_TRAIT_ALIAS – 0:AST_METHOD_REFERENCE • AST_GROUP_USE – グループ化されたuse (use A{B,C AS D}) 0:AST_ZVAL(ブレース前) 1:AST_USE(ブレース内)
  • 52. 通常ノード(4) /* 3 child nodes */ • AST_METHOD_CALL – メソッド呼び出し • AST_STATIC_CALL – 静的メソッド呼び出し • AST_CONDITIONAL – 三項演算子 および ?: • AST_TRY – try 0:AST_STMT_LIST(try ブロック) 1:AST_CATCH_LIST 2:AST_STMT_LIST(finally ブロッ ク)|NULL • AST_CATCH – catch 0:AST_ZVAL(catch するクラス 名) 1:AST_ZVAL(変数名) 2:AST_STMT_LIST(catch ブロック) / 7.1 になって、 0 は AST_NAME_LIST に変更 (複数指定できるようになったの で) • AST_PARAM – 引数定義 0:AST_ZVAL(型)|NULL 1:AST_ZVAL(変数名) 2:AST_ZVAL(デ フォルト値)|NULL ref_flag? • AST_PROP_ELEM – プロパティ定義 0:AST_ZVAL(プロパティ 名) 1:expr(値) 2:AST_ZVAL(doc_comment) • AST_CONST_ELEM – 定数定義 0:AST_ZVAL(定数名) 2:expr(定数値) /* 4 child nodes */ • AST_FOR – for(0;1;2) {3} • AST_FOREACH – foreach(0 as 1=>2){3} あるいは foreach(0 as 1){3} (2未使用)
  • 53. コードとASTの対比 function hello($name){ echo "Hello $name"; } hello('php'); 型 デフォルト引数 戻り値の型 未使用
  • 54. HHVM における AST • AST ノードの基底クラスである HPHP::Construct があり、Statement と Expression に分かれる • HPHP::Compiler::Parser::parseImpl が、 parseImpl7 あるいは parseImpl5 を呼び 出し、HPHP::Compiler::Parser::m_tree に StatementList が作られる • zend_ast_kind のそれぞれに対応するクラ スがある感じ
  • 58. 構文が変化すれば構造が変わる • Short List Syntax – (ZEND_)AST_LIST 廃止 – AST_ARRAY: attr に array 形式を保持 • Class Constant Visibility – AST_CONST_DECL: attr にアクセス権を保持 – AST_CONST_ELEM: 2child から 3childに • Catching Multiple Exception – catch (E $e) -> catch (E1|E2 $e) – AST_CATCH の 1番目の子要素が ZVAL 単体だったが、 7.1 で AST_NAME_LIST -> AST_ZVAL に ZEND_ARRAY_SYNTAX_LIST (list) ZEND_ARRAY_SYNTAX_LONG (array) ZEND_ARRAY_SYNTAX_SHORT ([])
  • 59. 逆変換可能 • 元のコードと等価なコードを作れる – 括弧やインデントは最低限必要なものだけ • zend_ast_export 関数 – assert に利用されている – 後述する Astkit::export で利用可能 • 自前で用意すれば別のコードにもできる
  • 60. 専用のメモリ空間 • CG(ast_arena) – zend_arena_createによって確保 – 初期サイズは 32MB / 必要に応じて拡張 • zend_ast_alloc 関数 – AST生成用のメモリを割り当てるための関数 – zend_arena_alloc を利用し、 CG(ast_arena) か らメモリを割り当てる(足りなければ拡張する) • Opcode生成後に解放 – zend_arena_destroyにて解放
  • 61. 短命 • parse時に作られてOpcodeを生成したら破棄 される • 現存の拡張は、parse後にzval(phpから扱え る変数)に変換することで php スクリプト から利用可能にしている • zend_ast_process 関数ポインタを使って フック可能 – AST構築直後 (Opcode 生成前)に呼ばれる – 拡張を書けば AST 改変も可能
  • 62. この章のまとめ • ASTノードの主な構成要素は種別、付属情報、 行番号、子ノード • 4つに大別 特殊,定義,リスト,通常 • バージョン間での互換性は考えられていない • 拡張からであればいろいろといじれる – 現存する拡張だけではできないこともある
  • 65. php-ast • https://github.com/nikic/php-ast • astparse_file あるいは astparse_code で AST 構築 • astNode をベースクラスとした astDecl • リスト型のノード は Node に統合 • Zval型のノードは Node の exprプロパティ • STMT_LIST(A) の子要素に STMT_LIST(B) が含 まれる場合は、B の子を A の子として併合
  • 66. astkit • https://github.com/sgolemon/astkit • AstKit::parseString あるいは AstKit::parseFile で AST構築 • AstKit をベースクラスとした AstKitList, AstKitDecl, AstKitZval にマッピングさ れる • $AstKit->export でコードに変換
  • 67. astparse_code('<?php 1 + 2;') 全ノードをphp スクリプトで扱える構造に変換 (CG(ast) は破棄) C言語 (CG(ast)) array astnode kind: 520 flags: 1 lineno: 1 left: 1 right: 2 astNode kind: 133 flags:0 lineno: 0 children: AST_ZVAL 1 AST_ZVAL 2 AST_BINA RY_OP + AST_STMT _LIST ast_to_zval php スクリプト (zval)
  • 68. Astkit::parseString('1+2;') 先頭のノードのみ生成。操作により子の AstKit が生成される C language (CG(ast) = astkit_tree->tree) AstKitList AST_ZVAL 1 AST_ZVAL 2 AST_BINA RY_OP + AST_STMT _LIST php script (zval) AstKit AstKitZval getChild(0) で生成 getChild(0,false) で生成 getChild(0) ならば int(1)
  • 69. それぞれの特徴 • php-ast – php スクリプトから扱いやすい – 初期のコストが大きめ – 異なるバージョンでの変換処理を拡張側で頑張っ てる部分もある • astkit – C の ast そのままのメモリを操作 – 利用する箇所が部分的ならば低コストか – ast 構造の変化によって php 側での操作が大き く変わる
  • 70. 利用例 • https://github.com/etsy/phan • php-ast を利用した静的解析ツール • 詳細は別セッションでされてるんじゃな いかな
  • 72. phpast • https://github.com/do-aki/phpast • 勉強目的で作った • ASTの可視化: phpast + graphviz https://dooakitestapp.herokuapp.com/phpa st/webapp/ • php のコードで ast を操作して、実行する予定 のコードを改変することができたら面白いよなぁ という妄想をしつつ、いまだ妄想のまま
  • 73. 今後考えられる AST 利用例 • さらなる最適化 – まだまだ静的に解決できる個所はある – この拡張を導入するだけで速くなる! なんてことも • コンバータ(トランスパイラ) (php7 -> hack) – php7 のコードから型推論できれば、あるいは – cf: https://speakerdeck.com/anatoo/type- inference-on-php • syntax grep (一致する構文を探索,置換) • power assert (実行過程をひとつひとつ出力)
  • 74. AST導入により 今後考えられる php の進化 • コード(AST)を受け取ることができる関数 – 今は assert のみ – ユーザランドでこれができると面白い – php のコードがそのままSQLになったり • ソースコードフィルタ – ルールに従って AST を入れ替える – 難読化に使える? ※ただの妄想です

Notes de l'éditeur

  1. ほかにも zval, list, decl といった構造体もあるが、 kind, attr を持ち、lineno を包含し、 0以上の子ノードを持つ というのは同じ
  2. ruby は 105種