
# 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](https://github.com/nikic/PHP-Parser) menjadi bentuk AST yang sama dengan sumber yang dicari, lalu dicocokkan simpul demi simpul.

Model ini sama dengan [ast-grep](https://ast-grep.github.io/), dan pembungkus kompatibilitas `sg` menyediakan antarmuka ast-grep CLI di atas mesin yang sama.

## Contoh singkat

```bash
# 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:

```bash
./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:

```bash
# 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.

```bash
./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](https://greph.inline0.dev/id/docs/indexed/ast) dan [pencarian AST dalam cache](https://greph.inline0.dev/id/docs/indexed/cache) melewati sebagian besar langkah 2 dan 3.
