ASTインデックス検索
ASTインデックス検索は、AST検索にキャッシュを追加したモードです。最初の呼び出しではノード単位の情報(呼び出し名、インスタンス化、クラス宣言、メソッド宣言)をディスク上のストアに抽出します。以降の呼び出しではこのストアを使い、構造パターンに一致する可能性が明らかにないファイルを除外します。その後、一致候補をソースに照らして再検証します。
ファクトインデックスは、greph-index ast-index CLIサブコマンドとGreph::buildAstIndex() / Greph::searchAstIndexed()ファサードメソッドから利用できます。事前作成済みインデックスを使った検索や、マニフェストを使ったセット検索にも対応しています。
インデックスの作成
# Full build at the current directory
./vendor/bin/greph-index ast-index build .
# Full build at an explicit root
./vendor/bin/greph-index ast-index build path/to/repo
# Use a non-default index directory
./vendor/bin/greph-index ast-index build . --index-dir /tmp/greph-ast-index
# Build a mutable overlay that can refresh cheaply on demand
./vendor/bin/greph-index ast-index build . \
--lifecycle opportunistic-refresh \
--auto-refresh-max-files 32 \
--auto-refresh-max-bytes 1048576
デフォルトでは、ファクトインデックスは<root>/.greph-ast-index/に保存されます。
作成に成功すると、概要が1行で表示されます:
Built AST index for 2547 files in .greph-ast-index (84931 fact rows, +2547 ~0 -0 =0)
4つのカウンターには、前回の作成以降に追加、更新、削除されたファイル数と、変更されていないファイル数が表示されます。
インデックスの更新
./vendor/bin/greph-index ast-index refresh .
refreshはインデックス対象のルートを再走査し、変更されたファイルを再解析して、変更があったファクト行のみを書き換えます。出力にはテキストインデックスと同じ4つのカウンターが使われます。
ライフサイクルプロファイルの動作は、事前作成済みテキストインデックスと同じです:
static: 鮮度の確認も自動変更も行わないmanual-refresh: 古い状態をstatsで報告するが、検索中は更新しないopportunistic-refresh: 変更対象が十分少ない場合に限り、検索中に更新するstrict-stale-check: 更新せず、古いインデックスでの検索を拒否する
インデックスの検索
./vendor/bin/greph-index ast-index search 'new $CLASS()' src
./vendor/bin/greph-index ast-index search '$obj->$method($$$ARGS)' src
./vendor/bin/greph-index ast-index search --json 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search -l 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search --trace-plan 'new $CLASS()' src
メタ変数の構文は、標準のAST検索と同じです。詳しい文法はモード / AST検索を参照してください。
オプション
| フラグ | 意味 |
|---|---|
--lang NAME |
ASTの言語。デフォルト: php |
-j N、--jobs N |
並列ワーカー数 |
-l、--files-with-matches |
一致したファイルのみ一覧表示 |
--glob GLOB |
パスがGLOBに一致するファイルのみを含める |
--type NAME / --type-not NAME |
ファイル形式フィルター |
--json |
JSON形式で出力 |
--no-ignore |
.gitignoreと.grephignoreを無視 |
--hidden |
隠しファイルを含める |
--strict-parse |
解析エラーをスキップせず、エラーとして終了 |
--fallback MODE |
インデックスがない場合の動作: デフォルトのfail、またはscan |
--index-dir DIR |
デフォルト以外のインデックスディレクトリを使用 |
--trace-plan |
事前作成済みASTのプランナー診断をstderrに出力 |
--show-index-origin |
複数インデックスまたはセットモードで、一致したインデックスのラベルを出力の先頭に付ける |
インデックスがない場合のフォールバック
デフォルトでは、指定場所にインデックスがない場合、ast-index searchはエラーになります。--fallback scanを渡すと、代わりに標準のASTスキャンを実行します。インデックスがあれば利用し、まだ作成されていない場合も検索結果を返したいツールに適した設定です。
./vendor/bin/greph-index ast-index search --fallback scan 'new $CLASS()' src
複数インデックスの検索
--index-dirを繰り返し指定すると、1つのコマンドで複数の事前作成済みASTインデックスを検索できます。WordPressのコアにプラグインやテーマを加えた構成などで便利です。
./vendor/bin/greph-index ast-index search \
--index-dir ../wordpress/.greph-ast-index \
--index-dir ./.greph-ast-index \
--show-index-origin \
'new $CLASS()' .
--show-index-originを有効にすると、出力の先頭に一致した事前作成済みインデックスのエントリー名が表示され、どのルートで見つかったかがわかります。
マニフェストを使ったインデックスセット
安定した事前作成済みインデックスの構成では、マニフェストを定義し、グループ化されたインデックスをgreph-index setに処理させます:
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --mode ast-index --dry-refresh
./vendor/bin/greph-index set search --mode ast-index --show-index-origin 'new $CLASS()' .
セット用サブコマンドは、直接ast-indexを使う場合と同じライフサイクル処理、古さの確認、プランナー診断に対応しています。
プランナーの診断
--trace-planは、完全なマッチャーを実行する前にGrephがクエリをどう絞り込んだかを表示します。ast-indexでは、通常次の情報が含まれます:
- パターンから抽出された大まかなファクトシグネチャ
- 検索した事前作成済みインデックスの数
- ファクトフィルターで除外されたファイル数
- 正確な構造検証のために再解析した候補ファイル数
インデックスに保存される情報
インデックス対象の各ファイルについて、ASTファクト抽出器は「重要な」ノードごとに1行を記録します。ファクトの種類は、ASTパターンでよく対象になる構文を網羅しています:
- 関数とメソッドの呼び出し(呼び出し名別)
- クラスのインスタンス化(クラス名別)
- クラス、インターフェース、トレイト、enumの宣言(名前別)
- 関数宣言(名前別)
- メソッド宣言(名前と可視性別)
- use文
クエリを受け取ると、Greph\Ast\AstFactQueryは検索パターンを大まかなファクトシグネチャ(例:「任意のクラスのnewを含む」)に変換し、ファクトストアと照合します。残ったファイルを再解析し、完全なパターンと照合します。
つまり、ファクトインデックスは完全な一致情報を保存するものではなく、候補を絞り込むための事前フィルターです。一致処理ではソースを引き続き解析するため、構造の正確さは標準のAST検索と同じで、候補の数だけが減ります。
使用する場面
次のような場合は、ASTインデックスモードが適しています:
- 同じコードベースに対して同じ構造クエリを繰り返し実行する
- パターンが、ファクト抽出器の保存する識別可能な形(呼び出し名、クラス名、宣言の種類)を対象にしている
- すべてのファイルを再解析するより、待ち時間を短縮したい
パターンを区別する特徴がファクトセットに含まれない場合(例: 識別可能な先頭部分を持たない複雑な入れ子式)は、ASTキャッシュモードが一般に適しています。大まかなシグネチャに頼らず、解析の手順そのものを省略できます。
プログラムからの利用
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;
Greph::buildAstIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);
$matches = Greph::searchAstIndexed(
'new $CLASS()',
'src',
new AstSearchOptions(jobs: 4, tracePlan: true),
);
searchAstIndexedは標準のAST検索と同じAstSearchOptionsを受け取り、同じ形式のlist<AstMatch>を返します。複数インデックスやマニフェストを使う場合は、searchAstIndexedMany(...)とsearchAstIndexedSet(...)を使用してください。