Greph
純粋なPHPコード検索、構造検索、書き換えエンジン。grep、ripgrep、ast-grepのワークフローを1つのComposerパッケージにまとめます。
composer require greph/greph最新リリース v0.2.3 MIT PHP 8.2+
# Any method call with any arguments
vendor/bin/greph -p '$obj->$method($$$ARGS)' src
# Any function declaration with a void return type
vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src
# Old-style array calls
vendor/bin/greph -p 'array($$$ITEMS)' src実践例
Composerでパッケージをインストールしてから、コマンドラインまたはPHPで検索や書き換えを実行できます。
検索、マッチ、書き換えのプレビュー
-Fは固定文字列のテキスト検索を実行し、-pはPHP形式のパターンと$CLASSなどのメタ変数を受け付け、-rは置換テンプレートを追加します。--dry-runを使うと、ファイルに書き込まずに書き換え後の内容を表示できます。
静的なGrephファサードから、PHPコードでも同じ操作を利用できます。依存関係としてインストールする場合、vendor/binコマンドにはバージョン0.2.3以降が必要です。
# Text search
vendor/bin/greph -F "function" src
# Structural search
vendor/bin/greph -p 'new $CLASS()' src
# Structural rewrite preview
vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --dry-run srcuse Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Text\TextSearchOptions;
$matches = Greph::searchText(
'function',
'src',
new TextSearchOptions(fixedString: true),
);
$asts = Greph::searchAst(
'new $CLASS()',
'src',
new AstSearchOptions(),
);純粋なPHP
検索と書き換えはgrep、ripgrep、ast-grepのバイナリなしでPHP 8.2+上で実行できます。json、ctype、tokenizer拡張機能と、キャッシュされたASTストア用のzlibを使用します。
テキスト検索
単語全体、大文字小文字を区別しない検索、コンテキスト、件数、ファイルのみの各モードに対応した固定文字列・正規表現検索。grep互換の出力。
AST検索
PHPソースを対象にした構造検索。$VARおよび$$$VARIADICメタ変数、繰り返しキャプチャ、JSON出力に対応しています。サポート対象の言語はPHPのみです。
AST書き換え
ドライラン、対話型、書き込みモードに対応した構造的な書き換え。マッチしたコードだけが変更され、マッチ範囲外のコメントや配置はそのままです。
インデックスモード
トライグラムテキストインデックス、ASTファクトインデックス、またはキャッシュ済みASTストアを一度構築すれば、再利用できます。プロジェクトのベンチマークでは、ASTファクトインデックスの構築に1.4 sかかり、new $CLASS()を2.9 sから11 msに短縮しました。トライグラムインデックスの構築には12.3 sかかりました。
rg・sgラッパー
vendor/bin/rgとvendor/bin/sgは、同じエンジン上で一般的なripgrepおよびast-grep検索フラグを受け付けます。未対応の機能:rgの複数行検索、--replace、--type-add、--sort、およびsgのYAMLルールとPHP以外の言語。
PHP API
Greph::searchText()、searchAst()、rewriteAst()は型付きの結果オブジェクトを返すため、PHPコードやエージェントはサブプロセスを使わずにエンジンを呼び出せます。
オラクルコーパス
39のシナリオで、grephのテキスト、AST、書き換えの出力をgrep、ripgrep、ast-grepと比較します。リポジトリをクローンしてcomposer test:oracleで実行できます。CIではユニットテストと統合テストを実行します。
Grephを選ぶ理由
ネイティブバイナリ不要
検索と書き換えはPHPプロセス内で実行されます。rg、grep、sgのバイナリは、任意のベンチマーク比較にのみ使用します。
すべてのモードで1つのエンジン
テキスト検索、PHP構造検索、書き換えを1つのComposerパッケージにまとめています。rgとsgのラッパーは、vendor/bin/grephと同じエンジンを呼び出します。
書き換えるのはマッチした箇所だけ
書き換え処理は、レンダリングしたテンプレートをマッチしたバイト範囲に挿入します。マッチ範囲外のコードでは、インデント、コメント、空行が保たれます。
デーモン不要のウォームインデックス
インデックスはディスクに保存され、実行間で再利用されます。ライフサイクルプロファイルに応じて、古いインデックスをそのまま残すか、検索中に更新するか、使用を拒否します。
との比較
Grephは検索と書き換えをPHP内で行います。これらのツールも関連する問題を解決しますが、Grephにはない機能もあります。
| ツール | 得意なこと | Grephとの違い |
|---|---|---|
| ripgrep | 得意なことWindows、macOS、Linuxでの確かなサポートと、各リリースのバイナリダウンロードを備えた再帰的な正規表現検索ツールです。gitignoreルールに従い、デフォルトで隠しファイルとバイナリファイルをスキップします。 | Grephとの違いGrephはComposerでインストールでき、PHPで実行できます。WordPressコーパスを使ったCIベンチマークでは、rgはすべてのコールドテキスト検索でより高速でした。たとえば、リテラル検索は81.62 msで完了し、rgは458.82 msでした。ウォーム状態のGrephインデックスでは、件数検索に10.90 msかかり、rgでは49.61 msでした。 |
| ast-grep | 得意なことtree-sitterを基盤とする、構造検索、lint、書き換えのためのコンパイル済みCLIです。多くの言語を組み込みでサポートし、その中にはPHPも含まれます。また、lintルールやコード変更用のYAMLルールファイルにも対応します。 | Grephとの違いGrephはPHPのみを扱い、YAMLルールは読み込みません。コールドスキャンでは、GrephのCIベンチマークでarray($$$ITEMS)に対してsgの方が高速でした。GrephのウォームASTインデックスは、4093.61 msのsgに対し、new $CLASS()を10.89 msで処理しました。 |
| Rector | 得意なことルールとルールセットを使ってPHPコードをアップグレード、リファクタリングします。PHPのバージョン5.3~8.5をカバーし、Symfony、PHPUnit、Doctrineなどのプロジェクトに対応します。 | Grephとの違いGrephは、作成したパターンとテンプレートに基づいて書き換えます。ドキュメントでは、変更がPHP-Parserで単一ファイルから解決できない型情報に依存する場合や、ファイル間でインポートを追加または削除する必要がある場合には適さないとしています。 |
| PHP CS Fixer | 得意なことコード規約に従うようコードを修正します。PHP-FIGのPERコーディングスタイルなどのルールセットを組み込みで備えています。 | Grephとの違いGrephはファイルを再フォーマットしません。パターンにマッチしたコードだけを変更します。フォーマッターが必要な場合は、ドキュメントでPHP CS Fixerを案内しています。 |
質問と回答
Grephのドキュメントから、簡潔に回答します。
いいえ。GrephはPHP内で検索と書き換えを実行します。外部のrg、grep、sgバイナリは、任意のベンチマーク比較にのみ使用します。
PHPのみです。パターンとソースファイルはnikic/php-parserで解析され、その他の--lang値はすべて未対応として拒否されます。
コールドスキャンでは高速ではありません。WordPressコーパスを使ったプロジェクトのCIベンチマークでは、コールド状態のテキスト検索すべてでrgとgrepのほうが高速でした。ウォームインデックスは繰り返し検索に役立ちます。たとえばインデックスを使った件数検索では、rgの49.61 msに対して10.90 msでした。
Grephは単一プロセスで実行されます。pcntl_forkがない場合、ワーカープールはすべてのチャンクをプロセス内で処理します。結果は-j 1の場合と同じで、エラーは発生しません。
マッチ範囲外は再フォーマットされません。マッチしたバイト範囲外のコードでは、空白、コメント、配置が保たれます。マッチしたコードはテンプレートから再出力されるため、改行は正規化され、範囲内のコメントが失われる場合があります。まず--dry-runで実行すると、ファイルに書き込まずに書き換え後の内容を表示できます。
デフォルトでは、インデックス対象ルートの.greph-index、.greph-ast-index、.greph-ast-cacheに保存されます。別の場所を使うには、greph-indexサブコマンドに--index-dirを渡してください。
Grephのドキュメントでは、CLIリファレンス、PHP API、インデックスモード、rgとsgのラッパーについて説明しています。