
# Recherche AST

Le mode AST de Greph recherche le code source PHP par structure, et non par texte. Les motifs s'écrivent comme du code PHP ordinaire avec des métavariables `$VARIABLE` et `$$$VARIADIC`. Le motif est analysé par [nikic/php-parser](https://github.com/nikic/PHP-Parser) pour obtenir la même forme d'AST que le code source recherché, puis les nœuds sont comparés un à un.

Ce modèle est le même que celui de [ast-grep](https://ast-grep.github.io/), et la couche de compatibilité `sg` expose l'interface CLI de ast-grep sur le même moteur.

## Exemples rapides

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

## Syntaxe des motifs

Un motif est du code source PHP valide. Partout où une expression ou une instruction réelle pourrait apparaître, vous pouvez substituer une métavariable pour capturer ce nœud.

| Syntaxe | Correspond à | Exemple |
|---|---|---|
| `$VAR` | Tout nœud AST unique, capturé sous le nom `VAR` | `$x + $y` correspond à `foo() + bar` |
| `$_` | Tout nœud AST unique, sans capture | `$_->method()` correspond à n'importe quel receveur |
| `$$$ARGS` | Zéro ou plusieurs nœuds (variadique), capturés sous le nom `ARGS` | `func($$$ARGS)` correspond à `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (répété) | Le même `$VAR` doit correspondre à la même structure aux deux occurrences | `$x == $x` correspond à `a == a`, mais pas à `a == b` |

Par convention, le nom de la métavariable est en majuscules, mais ce n'est pas obligatoire. `$_` est un joker sans capture.

`$$$VARIADIC` fonctionne partout où PHP autorise une liste de nœuds : arguments de fonction, éléments de tableau, listes de paramètres, corps d'instructions. La capture enregistre toute la séquence correspondante, même si elle est vide.

## Métavariables d'identifiant

Les noms de classes, de fonctions, d'interfaces, de traits et d'énumérations sont des identifiants, et non des expressions ; PHP n'accepterait donc normalement pas `$Name` après `class`. Greph réécrit ces motifs avant l'analyse pour que la mét variable fonctionne tout de même :

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

La même réécriture s'applique à `class`, `interface`, `trait`, `enum` et `function`.

## Métavariables répétées

Réutiliser une métavariable dans un même motif impose que ses deux occurrences correspondent structurellement au même nœud. C'est utile pour repérer du code redondant :

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

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

La même règle s'applique à `$$$VARIADIC` : deux occurrences de `$$$ARGS` doivent correspondre à la même séquence.

## Filtrer les fichiers

Le mode AST parcourt l'arborescence des fichiers avec le même outil que le mode textuel et applique par défaut le filtre de type de fichier `php`. Remplacez-le avec `--type`, `--type-not`, `--glob`, `--no-ignore` et `--hidden`. Le parcours respecte toujours `.gitignore` et `.grephignore` et ignore les fichiers binaires.

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

## Sortie

Le format de sortie par défaut est le même que celui de `file:line:content` en mode textuel, avec le code correspondant regroupé sur une seule ligne :

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

Passez `--json` pour produire les correspondances structurées :

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

## Erreurs d'analyse

Par défaut, Greph ignore silencieusement les fichiers dont l'analyse échoue. C'est le comportement adapté pour parcourir une grande base de code contenant du code PHP expérimental ou généré. Passez `--strict-parse` (avec `greph-index ast-index search` et `greph-index ast-cache search`) pour renvoyer l'erreur d'analyse à la place, ou définissez `skipParseErrors: false` sur `AstSearchOptions` en utilisant directement la façade.

## Utilisation par programmation

```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` correspond à l'`PhpParser\Node` capturée (vous pouvez donc la parcourir avec PHP-Parser de `NodeVisitor`), et `$match->captures` associe chaque nom de métavariable au nœud ou à la liste de nœuds capturés.

## Fonctionnement

Le pipeline est le suivant :

1. **Analyse du motif** : `Greph\Ast\PatternParser` réécrit les métavariables d'identifiant, puis analyse le motif avec PHP-Parser.
2. **Analyse du code source** : chaque fichier candidat est analysé une seule fois. En mode `ast-cache`, cette étape est remplacée par une désérialisation depuis le cache.
3. **Filtrage des candidats** : `Greph\Ast\AstPatternPrefilter` extrait une signature générale du motif (nom d'appel, nom de classe, type d'instruction) et s'en sert pour ignorer les fichiers qui ne peuvent manifestement pas contenir de correspondance. En mode `ast-index`, cette étape interroge plutôt un stockage de faits pré calculé.
4. **Comparaison** : `Greph\Ast\PatternMatcher` parcourt l'AST du code source et le compare à l'AST du motif, en liant les métavariables au passage et en vérifiant la cohérence des variables répétées.
5. **Production des résultats** : les nœuds correspondants sont convertis en objets `AstMatch` avec leurs positions en lignes et en octets ainsi que l'extrait source d'origine.

Pour les charges de travail répétées, la [recherche AST indexée](https://greph.inline0.dev/fr/docs/indexed/ast) et la [recherche AST avec cache](https://greph.inline0.dev/fr/docs/indexed/cache) ignorent la plupart des étapes 2 et 3.
