
# Búsqueda AST

El modo AST de Greph busca código fuente PHP por su estructura, no por su texto. Los patrones se escriben como código PHP normal, con metavariables `$VARIABLE` y `$$$VARIADIC`. [nikic/php-parser](https://github.com/nikic/PHP-Parser) analiza el patrón y genera la misma estructura AST que la del código fuente donde busca; después compara los nodos uno por uno.

Este es el mismo modelo que [ast-grep](https://ast-grep.github.io/), y la capa de compatibilidad `sg` ofrece la interfaz CLI de ast-grep sobre el mismo motor.

## Ejemplos rápidos

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

## Sintaxis de patrones

Un patrón es código fuente PHP válido. En cualquier lugar donde pueda ir una expresión o instrucción real, puedes sustituir una metavariable para capturar ese nodo.

| Sintaxis | Coincide con | Ejemplo |
|---|---|---|
| `$VAR` | Cualquier nodo AST individual, capturado como `VAR` | `$x + $y` coincide con `foo() + bar` |
| `$_` | Cualquier nodo AST individual, sin capturarlo | `$_->method()` coincide con cualquier receptor |
| `$$$ARGS` | Cero o más nodos (variádicos), capturados como `ARGS` | `func($$$ARGS)` coincide con `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (repetido) | El mismo `$VAR` debe coincidir con la misma estructura en ambas ocasiones | `$x == $x` coincide con `a == a`, pero no con `a == b` |

Por convención, el nombre de la metavariable va en mayúsculas, pero no es obligatorio. `$_` es un comodín que no captura.

`$$$VARIADIC` funciona dondequiera que PHP permita una lista de nodos: argumentos de funciones, elementos de matrices, listas de parámetros y cuerpos de instrucciones. La captura almacena toda la secuencia coincidente, incluso si no contiene elementos.

## Metavariables de identificador

Los nombres de clases, funciones, interfaces, traits y enums son identificadores, no expresiones, por lo que PHP normalmente no aceptaría `$Name` después de `class`. Greph reescribe estos patrones antes del análisis para que la metavariable siga funcionando:

```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 misma reescritura se aplica a `class`, `interface`, `trait`, `enum` y `function`.

## Metavariables repetidas

Reutilizar una metavariable dentro del mismo patrón exige que ambas apariciones coincidan estructuralmente con el mismo nodo. Esto resulta útil para detectar código redundante:

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

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

La misma regla se aplica a `$$$VARIADIC`: dos apariciones de `$$$ARGS` deben coincidir con la misma secuencia.

## Filtrado de archivos

El modo AST recorre el árbol de archivos con el mismo explorador que el modo de texto y, de forma predeterminada, filtra por el tipo de archivo `php`. Anula este valor con `--type`, `--type-not`, `--glob`, `--no-ignore` y `--hidden`. El explorador sigue respetando `.gitignore` y `.grephignore`, y omite los archivos binarios.

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

## Salida

El formato de salida predeterminado usa el mismo formato `file:line:content` que el modo de texto y presenta el código coincidente en una sola línea:

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

Pasa `--json` para emitir coincidencias estructuradas:

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

## Errores de análisis

De forma predeterminada, Greph omite silenciosamente los archivos que no se pueden analizar. Este es el comportamiento adecuado al recorrer una base de código grande que contiene código PHP experimental o generado. Pasa `--strict-parse` (en `greph-index ast-index search` y `greph-index ast-cache search`) para generar el error de análisis, o establece `skipParseErrors: false` en `AstSearchOptions` al usar directamente la fachada.

## Uso programático

```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` es el `PhpParser\Node` capturado (así que puedes recorrerlo con PHP-Parser de `NodeVisitor`), y `$match->captures` asigna cada nombre de metavariable al nodo o la lista de nodos capturados.

## Cómo funciona

El proceso es el siguiente:

1. **Análisis del patrón**: `Greph\Ast\PatternParser` reescribe las metavariables de identificador y luego analiza el patrón con PHP-Parser.
2. **Análisis del código fuente**: cada archivo candidato se analiza una vez. En el modo `ast-cache`, este paso se sustituye por la deserialización desde el almacén de caché.
3. **Filtrado de candidatos**: `Greph\Ast\AstPatternPrefilter` extrae una firma general del patrón (nombre de llamada, nombre de clase, tipo de instrucción) y la usa para omitir archivos que claramente no pueden contener una coincidencia. En el modo `ast-index`, este paso consulta un almacén de hechos precalculado.
4. **Comparación**: `Greph\Ast\PatternMatcher` recorre el AST del código fuente y lo compara con el AST del patrón; vincula las metavariables y comprueba la coherencia de las variables repetidas.
5. **Generación de resultados**: los nodos coincidentes se convierten en objetos `AstMatch` con posiciones de línea y byte, y el fragmento original del código fuente.

En cargas de trabajo repetidas, la [búsqueda AST indexada](https://greph.inline0.dev/es/docs/indexed/ast) y la [búsqueda AST en caché](https://greph.inline0.dev/es/docs/indexed/cache) omiten la mayoría de los pasos 2 y 3.
