
# AST-zoeken

De AST-modus van Greph doorzoekt PHP-broncode op basis van structuur, niet op basis van tekst. Patronen worden geschreven als gewone PHP-code met metavariabelen `$VARIABLE` en `$$$VARIADIC`. [nikic/php-parser](https://github.com/nikic/PHP-Parser) parseert het patroon tot dezelfde AST-vorm als de broncode die wordt doorzocht en vergelijkt ze vervolgens knoop voor knoop.

Dit is hetzelfde model als [ast-grep](https://ast-grep.github.io/), en de compatibiliteitswrapper `sg` biedt de ast-grep-interface CLI boven op dezelfde engine.

## Snelle voorbeelden

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

## Patroonsyntaxis

Een patroon is geldige PHP-broncode. Overal waar een echte expressie of instructie kan staan, kun je een metavariabele invullen om die knoop vast te leggen.

| Syntaxis | Komt overeen met | Voorbeeld |
|---|---|---|
| `$VAR` | Elke afzonderlijke AST-knoop, vastgelegd als `VAR` | `$x + $y` komt overeen met `foo() + bar` |
| `$_` | Elke afzonderlijke AST-knoop, niet vastgelegd | `$_->method()` komt overeen met elke ontvanger |
| `$$$ARGS` | Nul of meer knopen (variadisch), vastgelegd als `ARGS` | `func($$$ARGS)` komt overeen met `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (herhaald) | Dezelfde `$VAR` moet beide keren overeenkomen met dezelfde structuur | `$x == $x` komt overeen met `a == a`, niet met `a == b` |

De naam van de metavariabele is volgens afspraak in hoofdletters, maar dat hoeft niet. `$_` is een jokerteken dat niets vastlegt.

`$$$VARIADIC` werkt overal waar PHP een lijst met knopen toestaat: functieargumenten, array-items, parameterlijsten en instructieblokken. De capture bevat de hele overeenkomende reeks, inclusief nul items.

## Identifier-metavariabelen

Namen van klassen, functies, interfaces, traits en enums zijn identifiers, geen expressies. Daarom accepteert PHP normaal gesproken geen `$Name` na `class`. Greph herschrijft deze patronen voordat ze worden geparseerd, zodat de metavariabele toch werkt:

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

Dezelfde herschrijving vindt plaats voor `class`, `interface`, `trait`, `enum` en `function`.

## Herhaalde metavariabelen

Als je een metavariabele binnen hetzelfde patroon opnieuw gebruikt, moeten beide voorkomens structureel met dezelfde knoop overeenkomen. Dit is handig om overbodige code op te sporen:

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

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

Dezelfde regel geldt voor `$$$VARIADIC`: twee voorkomens van `$$$ARGS` moeten overeenkomen met dezelfde reeks.

## Bestanden filteren

De AST-modus doorloopt de bestandsstructuur met dezelfde walker als de tekstmodus en gebruikt standaard het bestandstypefilter `php`. Overschrijf dit met `--type`, `--type-not`, `--glob`, `--no-ignore` en `--hidden`. De walker houdt nog steeds rekening met `.gitignore` en `.grephignore` en slaat binaire bestanden over.

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

## Uitvoer

De standaarduitvoer gebruikt dezelfde `file:line:content`-indeling als de tekstmodus, waarbij de overeenkomende code op één regel wordt samengevouwen:

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

Geef `--json` mee om overeenkomsten in een gestructureerd formaat uit te voeren:

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

## Parseerfouten

Standaard slaat Greph bestanden die niet kunnen worden geparseerd stilzwijgend over. Dat is het juiste gedrag bij het doorzoeken van een grote codebase met experimentele of gegenereerde PHP. Geef `--strict-parse` mee (bij `greph-index ast-index search` en `greph-index ast-cache search`) om in plaats daarvan de parseerfout te melden, of stel `skipParseErrors: false` in op `AstSearchOptions` wanneer je de facade rechtstreeks gebruikt.

## Programmatisch gebruik

```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` is de vastgelegde `PhpParser\Node` (zodat je erdoorheen kunt lopen met PHP-Parser-`NodeVisitor`), en `$match->captures` koppelt elke metavariabelenaam aan de vastgelegde knoop of lijst met knopen.

## Werking

De pipeline bestaat uit:

1. **Patroon parsen**: `Greph\Ast\PatternParser` herschrijft identifier-metavariabelen en parseert het patroon vervolgens met PHP-Parser.
2. **Broncode parsen**: elk kandidaatbestand wordt één keer geparseerd. In de modus `ast-cache` wordt deze stap vervangen door deserialisatie uit de cacheopslag.
3. **Kandidaten filteren**: `Greph\Ast\AstPatternPrefilter` haalt een grove signatuur uit het patroon (aanroepnaam, klassenaam, instructietype) en gebruikt deze om bestanden over te slaan die duidelijk geen overeenkomst kunnen bevatten. In de modus `ast-index` wordt in plaats daarvan een vooraf berekende feitenopslag doorzocht.
4. **Matchen**: `Greph\Ast\PatternMatcher` doorloopt de AST van de broncode en vergelijkt deze met de AST van het patroon. Daarbij worden metavariabelen gebonden en wordt afgedwongen dat herhaalde variabelen overeenkomen.
5. **Resultaten uitvoeren**: overeenkomende knopen worden omgezet in `AstMatch`-objecten met regel- en byteposities en het oorspronkelijke brondeel.

Bij herhaalde workloads slaan [zoeken in een geïndexeerde AST](https://greph.inline0.dev/nl/docs/indexed/ast) en [zoeken in een gecachte AST](https://greph.inline0.dev/nl/docs/indexed/cache) de meeste delen van stap 2 en 3 over.
