
# AST-haku

Grephn AST-tila etsii PHP-lähdekoodia rakenteen, ei tekstin perusteella. Kuviot kirjoitetaan tavallisena PHP-koodina, jossa käytetään `$VARIABLE`- ja `$$$VARIADIC`-metamuuttujia. [nikic/php-parser](https://github.com/nikic/PHP-Parser) jäsentää kuvion samaan AST-muotoon kuin etsittävä lähdekoodi, minkä jälkeen niitä verrataan solmu kerrallaan.

Tämä vastaa samaa mallia kuin [ast-grep](https://ast-grep.github.io/), ja `sg`-yhteensopivuuskerros tarjoaa ast-grepn CLI-rajapinnan saman moottorin päällä.

## Esimerkkejä

```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
```

## Kuvioiden syntaksi

Kuvion on oltava kelvollista PHP-lähdekoodia. Voit korvata metamuuttujalla minkä tahansa kohdan, johon voisi tulla oikea lauseke tai lause, ja kaapata näin kyseisen solmun.

| Syntaksi | Vastaa | Esimerkki |
|---|---|---|
| `$VAR` | Mitä tahansa yksittäistä AST-solmua, kaapataan muodossa `VAR` | `$x + $y` vastaa `foo() + bar` |
| `$_` | Mitä tahansa yksittäistä AST-solmua, ei kaapata | `$_->method()` vastaa mitä tahansa vastaanottajaa |
| `$$$ARGS` | Nolla tai useampia solmuja (vaihtuva määrä), kaapataan muodossa `ARGS` | `func($$$ARGS)` vastaa `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (toistuva) | Saman `$VAR`n on vastattava molemmilla kerroilla samaa rakennetta | `$x == $x` vastaa `a == a`, mutta ei `a == b` |

Metamuuttujan nimi kirjoitetaan tavan mukaan isoilla kirjaimilla, mutta niin ei tarvitse tehdä. `$_` on kaappaamaton yleismerkki.

`$$$VARIADIC` toimii kaikkialla, missä PHP sallii solmujen luettelon: funktion argumenteissa, taulukon alkioissa, parametriluetteloissa ja lauseiden rungoissa. Kaappaus tallentaa koko vastaavan jonon (myös tyhjän jonon).

## Tunnisteiden metamuuttujat

Luokkien, funktioiden, rajapintojen, traitien ja enumien nimet ovat tunnisteita eivätkä lausekkeita, joten PHP ei tavallisesti hyväksy `$Name`-merkkiä `class`-merkin jälkeen. Greph kirjoittaa nämä kuviot uudelleen ennen jäsentämistä, jolloin metamuuttuja toimii:

```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
```

Sama uudelleenkirjoitus tehdään merkeille `class`, `interface`, `trait`, `enum` ja `function`.

## Toistuvat metamuuttujat

Kun samaa metamuuttujaa käytetään kuviossa useita kertoja, kaikkien esiintymien on vastattava rakenteeltaan samaa solmua. Tämä auttaa löytämään tarpeetonta koodia:

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

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

Sama sääntö koskee myös `$$$VARIADIC`-merkintää: kahden `$$$ARGS`-esiintymän on vastattava samaa jonoa.

## Tiedostojen suodatus

AST-tila käy hakemistopuun läpi samalla läpikäyjällä kuin tekstihaku ja käyttää oletusarvoisesti tiedostotyyppisuodatinta `php`. Ohita se valitsimilla `--type`, `--type-not`, `--glob`, `--no-ignore` ja `--hidden`. Läpikäyjä noudattaa edelleen asetuksia `.gitignore` ja `.grephignore` ja ohittaa binaaritiedostot.

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

## Tuloste

Oletustuloste käyttää samaa `file:line:content`-muotoa kuin tekstihaku, ja osumakoodi on tiivistetty yhdelle riville:

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

Tulosta rakenteiset osumat valitsimella `--json`:

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

## Jäsennysvirheet

Oletusarvoisesti Greph ohittaa äänettömästi tiedostot, joita ei voi jäsentää. Tämä on sopiva toimintatapa, kun käyt läpi suurta koodikantaa, joka sisältää kokeellista tai automaattisesti luotua PHP-koodia. Lisää jäsennysvirhe valitsimella `--strict-parse` (komennoissa `greph-index ast-index search` ja `greph-index ast-cache search`) tai aseta `skipParseErrors: false` suoraan facadea käyttäessäsi kohteessa `AstSearchOptions`.

## Käyttö ohjelmallisesti

```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` on kaapattu `PhpParser\Node` (jota voit käydä läpi PHP-Parsern `NodeVisitor`-toiminnolla), ja `$match->captures` yhdistää kunkin metamuuttujan nimen sen kaapattuun solmuun tai solmujen luetteloon.

## Näin se toimii

Putki toimii näin:

1. **Kuvion jäsentäminen**: `Greph\Ast\PatternParser` kirjoittaa tunnisteiden metamuuttujat uudelleen ja jäsentää sitten kuvion komennolla PHP-Parser.
2. **Lähdekoodin jäsentäminen**: jokainen ehdokastiedosto jäsennetään kerran. `ast-cache`-tilassa tämä vaihe korvataan sarjoituksen purkamisella välimuistivarastosta.
3. **Ehdokkaiden suodatus**: `Greph\Ast\AstPatternPrefilter` poimii kuviosta karkean tunnisteen (kutsun nimi, luokan nimi, lauseen tyyppi) ja ohittaa sen avulla tiedostot, joissa ei selvästikään voi olla osumaa. `ast-index`-tilassa tässä vaiheessa kysytään tietoja esilasketusta faktavarastosta.
4. **Vastaavuuden etsiminen**: `Greph\Ast\PatternMatcher` käy läpi lähteen AST-rakenteen ja vertaa sitä kuvion AST-rakenteeseen, sitoo samalla metamuuttujat ja varmistaa toistuvien muuttujien vastaavuuden.
5. **Tulosten tuottaminen**: vastaavat solmut muunnetaan `AstMatch`-olioiksi, jotka sisältävät rivin ja tavun sijainnit sekä alkuperäisen lähdekoodiosuuden.

Toistuvissa työmalleissa [indeksoitu AST-haku](https://greph.inline0.dev/fi/docs/indexed/ast) ja [välimuistiin tallennettu AST-haku](https://greph.inline0.dev/fi/docs/indexed/cache) ohittavat suurimman osan vaiheista 2 ja 3.
