
# AST-sökning

Grephs AST-läge söker i PHP-källkod efter struktur, inte text. Mönstren skrivs som vanlig PHP-kod med metavariablerna `$VARIABLE` och `$$$VARIADIC`. Mönstret parsas av [nikic/php-parser](https://github.com/nikic/PHP-Parser) till samma AST-form som källkoden som genomsöks, och matchas sedan nod för nod.

Det här är samma modell som [ast-grep](https://ast-grep.github.io/), och kompatibilitetsomslaget `sg` exponerar ast-greps CLI-gränssnitt ovanpå samma motor.

## Snabba exempel

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

## Mönstersyntax

Ett mönster är giltig PHP-källkod. Överallt där ett riktigt uttryck eller en sats kan stå kan du ersätta det med en metavariabel som fångar upp den noden.

| Syntax | Matchar | Exempel |
|---|---|---|
| `$VAR` | Valfri enskild AST-nod, infångad som `VAR` | `$x + $y` matchar `foo() + bar` |
| `$_` | Valfri enskild AST-nod, utan infångning | `$_->method()` matchar vilken mottagare som helst |
| `$$$ARGS` | Noll eller fler noder (variadisk), infångade som `ARGS` | `func($$$ARGS)` matchar `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (upprepad) | Samma `$VAR` måste matcha samma struktur båda gångerna | `$x == $x` matchar `a == a`, inte `a == b` |

Metavariabelnamnet skrivs enligt konvention med versaler, men det behöver inte göra det. `$_` är ett jokertecken som inte fångas upp.

`$$$VARIADIC` fungerar där PHP tillåter en lista med noder: funktionsargument, arrayelement, parameterlistor och satskroppar. Infångningen lagrar hela den matchade sekvensen (även noll element).

## Identifierarmetavariabler

Klass-, funktions-, gränssnitts-, trait- och enum-namn är identifierare, inte uttryck. Därför accepterar PHP normalt inte `$Name` efter `class`. Greph skriver om dessa mönster före parsningen så att metavariabeln fortfarande fungerar:

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

Samma omskrivning görs för `class`, `interface`, `trait`, `enum` och `function`.

## Upprepade metavariabler

Om du återanvänder en metavariabel i samma mönster innebär det att båda förekomsterna måste matcha samma nod strukturellt. Det är användbart för att upptäcka redundant kod:

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

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

Samma regel gäller för `$$$VARIADIC`: två förekomster av `$$$ARGS` måste matcha samma sekvens.

## Filtrera filer

AST-läget går igenom filträdet med samma genomgångare som textläget och använder som standard filtret för filtypen `php`. Åsidosätt det med `--type`, `--type-not`, `--glob`, `--no-ignore` och `--hidden`. Genomgångaren respekterar fortfarande `.gitignore` och `.grephignore` och hoppar över binärfiler.

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

## Utdata

Standardutdata använder samma format `file:line:content` som textläget, med den matchade koden hopslagen till en enda rad:

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

Skicka med `--json` för att generera strukturerade matchningar:

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

## Parsefel

Som standard hoppar Greph tyst över filer som inte går att parsa. Det är rätt beteende när du kör mot en stor kodbas med experimentell eller genererad PHP. Skicka med `--strict-parse` (med `greph-index ast-index search` och `greph-index ast-cache search`) för att i stället utlösa parsefelet, eller ange `skipParseErrors: false` för `AstSearchOptions` när du använder fasaden direkt.

## Programmatisk användning

```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` är det infångade `PhpParser\Node` (så att du kan gå igenom det med PHP-Parsers `NodeVisitor`), och `$match->captures` mappar varje metavariabelnamn till dess infångade nod eller lista med noder.

## Så fungerar det

Kedjan består av:

1. **Mönsterparsning**: `Greph\Ast\PatternParser` skriver om identifierarmetavariabler och parsar sedan mönstret med PHP-Parser.
2. **Källparsning**: varje kandidatfil parsas en gång. I läget `ast-cache` ersätts det här steget med en deserialisering från cachen.
3. **Kandidatfiltrering**: `Greph\Ast\AstPatternPrefilter` extraherar en grov signatur från mönstret (anropsnamn, klassnamn, satstyp) och använder den för att hoppa över filer som uppenbart inte kan innehålla en matchning. I läget `ast-index` görs i stället en sökning i ett förberäknat faktalager.
4. **Matchning**: `Greph\Ast\PatternMatcher` går igenom källans AST och jämför den med mönstrets AST, binder metavariabler längs vägen och säkerställer att upprepade variabler stämmer överens.
5. **Resultatgenerering**: matchade noder omvandlas till objekt av typen `AstMatch` med rad- och bytepositioner samt det ursprungliga källkodsstycket.

För upprepade körningar hoppar [indexerad AST-sökning](https://greph.inline0.dev/sv/docs/indexed/ast) och [cachad AST-sökning](https://greph.inline0.dev/sv/docs/indexed/cache) över de flesta delarna av steg 2 och 3.
