Lewati ke konten

Pencarian AST

Mode AST milik Greph mencari struktur sumber PHP, bukan teks. Pola ditulis sebagai kode PHP biasa dengan metavariabel $VARIABLE dan $$$VARIADIC. Pola diurai oleh nikic/php-parser menjadi bentuk AST yang sama dengan sumber yang dicari, lalu dicocokkan simpul demi simpul.

Model ini sama dengan ast-grep, dan pembungkus kompatibilitas sg menyediakan antarmuka ast-grep CLI di atas mesin yang sama.

Contoh singkat

Shell
# Find every constructor call
./vendor/bin/greph -p 'new $CLASS()' src

# Find every method call with any number of arguments
./vendor/bin/greph -p '$obj->$method($$$ARGS)' src

# Find old-style array() calls
./vendor/bin/greph -p 'array($$$ITEMS)' src

# Find void-returning functions
./vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src

# Find isset ternaries that should be ??
./vendor/bin/greph -p 'isset($x) ? $x : $default' src

Sintaks pola

Pola adalah sumber PHP yang valid. Di mana pun ekspresi atau pernyataan biasa dapat ditempatkan, Anda dapat menggantinya dengan metavariabel untuk menangkap simpul tersebut.

Sintaks Cocok dengan Contoh
$VAR Simpul AST tunggal apa pun, ditangkap sebagai VAR $x + $y cocok dengan foo() + bar
$_ Simpul AST tunggal apa pun, tanpa ditangkap $_->method() cocok dengan penerima apa pun
$$$ARGS Nol atau lebih simpul (variadik), ditangkap sebagai ARGS func($$$ARGS) cocok dengan func(), func(1), func(1, 2, 3)
$VAR (berulang) $VAR yang sama harus cocok dengan struktur yang sama pada kedua kemunculan $x == $x cocok dengan a == a, bukan a == b

Nama metavariabel biasanya menggunakan huruf besar, tetapi tidak harus. $_ adalah wildcard yang tidak menangkap.

$$$VARIADIC berfungsi di mana pun PHP mengizinkan daftar simpul: argumen fungsi, item array, daftar parameter, isi pernyataan. Tangkapan menyimpan seluruh urutan yang cocok, termasuk jika tidak ada item.

Metavariabel pengenal

Nama kelas, fungsi, antarmuka, trait, dan enum adalah pengenal, bukan ekspresi, sehingga PHP biasanya tidak menerima $Name setelah class. Greph menulis ulang pola ini sebelum penguraian agar metavariabel tetap berfungsi:

Shell
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src

Penulisan ulang yang sama terjadi untuk class, interface, trait, enum, dan function.

Metavariabel berulang

Menggunakan kembali metavariabel di dalam pola yang sama menyatakan bahwa kedua kemunculannya harus cocok secara struktural dengan simpul yang sama. Ini berguna untuk menemukan kode yang berlebihan:

Shell
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src

# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src

Aturan yang sama berlaku untuk $$$VARIADIC: dua kemunculan $$$ARGS harus cocok dengan urutan yang sama.

Memfilter file

Mode AST menelusuri pohon file dengan walker yang sama seperti mode teks dan secara default menggunakan filter jenis file php. Ganti dengan --type, --type-not, --glob, --no-ignore, dan --hidden. Walker tetap mematuhi .gitignore dan .grephignore serta melewati file biner.

Shell
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src

Output

Output default menggunakan format file:line:content yang sama seperti mode teks, dengan kode yang cocok diringkas menjadi satu baris:

Text
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();

Berikan --json untuk menghasilkan kecocokan terstruktur:

JSON
[
  {
    "file": "src/Greph.php",
    "start_line": 38,
    "end_line": 38,
    "start_file_pos": 1234,
    "end_file_pos": 1252,
    "code": "new FileWalker()"
  }
]

Error penguraian

Secara default, Greph diam-diam melewati file yang gagal diurai. Perilaku ini tepat saat menjalankan pencarian pada basis kode besar yang berisi PHP eksperimental atau yang dihasilkan otomatis. Berikan --strict-parse (pada greph-index ast-index search dan greph-index ast-cache search) untuk memunculkan error penguraian, atau tetapkan skipParseErrors: false pada AstSearchOptions saat menggunakan facade secara langsung.

Penggunaan secara terprogram

PHP
use Greph\Greph;
use Greph\Ast\AstSearchOptions;

$matches = Greph::searchAst(
    '$obj->$method($$$ARGS)',
    'src',
    new AstSearchOptions(jobs: 4),
);

foreach ($matches as $match) {
    echo "{$match->file}:{$match->startLine}\n";
    echo $match->code . "\n";

    foreach ($match->captures as $name => $node) {
        echo "  {$name} = " . get_debug_type($node) . "\n";
    }
}

$match->node adalah PhpParser\Node yang ditangkap (sehingga Anda dapat menelusurinya dengan PHP-Parser milik NodeVisitor), dan $match->captures memetakan setiap nama metavariabel ke simpul atau daftar simpul yang ditangkap.

Cara kerjanya

Pipeline-nya adalah:

  1. Penguraian pola: Greph\Ast\PatternParser menulis ulang metavariabel pengenal, lalu mengurai pola dengan PHP-Parser.
  2. Penguraian sumber: setiap file kandidat diurai satu kali. Dalam mode ast-cache, langkah ini digantikan dengan deserialisasi dari penyimpanan cache.
  3. Pemfilteran kandidat: Greph\Ast\AstPatternPrefilter mengekstrak tanda tangan kasar dari pola (nama pemanggilan, nama kelas, jenis pernyataan) dan menggunakannya untuk melewati file yang jelas tidak mungkin memuat kecocokan. Dalam mode ast-index, langkah ini justru mengueri penyimpanan fakta yang telah dihitung sebelumnya.
  4. Pencocokan: Greph\Ast\PatternMatcher menelusuri AST sumber dan membandingkannya dengan AST pola, mengikat metavariabel dan memastikan konsistensi variabel berulang.
  5. Penghasilan hasil: simpul yang cocok diubah menjadi objek AstMatch dengan posisi baris/byte dan potongan sumber asli.

Untuk beban kerja berulang, pencarian AST terindeks dan pencarian AST dalam cache melewati sebagian besar langkah 2 dan 3.