
# AST 검색

Greph의 AST 모드는 PHP 소스 코드를 텍스트가 아닌 구조로 검색합니다. 패턴은 PHP 코드로 작성하며 `$VARIABLE` 및 `$$$VARIADIC` 메타변수를 사용합니다. 패턴은 [nikic/php-parser](https://github.com/nikic/PHP-Parser)에서 검색 대상 소스와 같은 AST 형태로 파싱된 다음 노드 단위로 매칭됩니다.

이는 [ast-grep](https://ast-grep.github.io/)와 같은 모델이며, `sg` 호환성 래퍼는 동일한 엔진 위에서 ast-grep의 CLI 기능을 제공합니다.

## 빠른 예제

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

## 패턴 구문

패턴은 유효한 PHP 소스 코드입니다. 실제 표현식이나 문장이 올 수 있는 곳이라면 어디든 메타변수로 해당 노드를 캡처할 수 있습니다.

| 구문 | 매치 대상 | 예시 |
|---|---|---|
| `$VAR` | 캡처된 `VAR`로 저장되는 모든 단일 AST 노드 | `$x + $y`는 `foo() + bar`와 매치됩니다 |
| `$_` | 캡처되지 않는 모든 단일 AST 노드 | `$_->method()`는 모든 리시버와 매치됩니다 |
| `$$$ARGS` | `ARGS`로 캡처되는 0개 이상의 노드 (가변) | `func($$$ARGS)`는 `func()`, `func(1)`, `func(1, 2, 3)`과 매치됩니다 |
| `$VAR` (반복) | 같은 `$VAR`가 두 위치에서 동일한 구조와 매치되어야 함 | `$x == $x`는 `a == a`와 매치되지만 `a == b`와는 매치되지 않습니다 |

메타변수 이름은 관례상 대문자지만 반드시 그럴 필요는 없습니다. `$_`는 캡처하지 않는 와일드카드입니다.

`$$$VARIADIC`는 PHP에서 노드 목록을 허용하는 모든 위치에서 사용할 수 있습니다. 함수 인수, 배열 항목, 매개변수 목록, 문장 본문이 여기에 해당합니다. 캡처에는 0개 항목도 포함해 매치된 전체 시퀀스가 저장됩니다.

## 식별자 메타변수

클래스, 함수, 인터페이스, 트레이트, 열거형 이름은 표현식이 아니라 식별자이므로 `class` 뒤에 `$Name`을 쓰면 일반적으로 PHP에서 허용되지 않습니다. Greph은 파싱 전에 패턴을 다시 작성해 메타변수를 사용할 수 있게 합니다:

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

`class`, `interface`, `trait`, `enum`, `function`에도 같은 재작성 과정이 적용됩니다.

## 반복 메타변수

같은 패턴에서 메타변수를 다시 사용하면 두 위치가 구조적으로 동일한 노드와 매치되어야 한다는 조건이 생깁니다. 이는 중복 코드를 찾을 때 유용합니다:

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

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

`$$$VARIADIC`에도 같은 규칙이 적용됩니다. `$$$ARGS`가 두 번 나오면 동일한 시퀀스와 매치되어야 합니다.

## 파일 필터링

AST 모드는 텍스트 모드와 동일한 워커로 파일 트리를 순회하며 기본적으로 `php` 파일 유형 필터를 사용합니다. `--type`, `--type-not`, `--glob`, `--no-ignore`, `--hidden`로 재정의할 수 있습니다. 워커는 여전히 `.gitignore` 및 `.grephignore`을 따르며 바이너리 파일은 건너뜁니다.

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

## 출력

기본 출력은 텍스트 모드와 동일한 `file:line:content` 형식을 사용하며, 매치된 코드를 한 줄로 접어 표시합니다:

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

구조화된 매치를 출력하려면 `--json`을 전달하세요:

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

## 파싱 오류

기본적으로 Greph은 파싱에 실패한 파일을 조용히 건너뜁니다. 실험용 또는 생성된 PHP이 포함된 대규모 코드베이스를 검색할 때 적합한 동작입니다. 대신 파싱 오류를 발생시키려면 (`greph-index ast-index search` 및 `greph-index ast-cache search`에서) `--strict-parse`를 전달하거나, 파사드를 직접 사용할 때 `AstSearchOptions`에서 `skipParseErrors: false`를 설정하세요.

## 프로그래밍 방식 사용

```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`는 캡처된 `PhpParser\Node`이며 (따라서 PHP-Parser의 `NodeVisitor`로 순회할 수 있습니다), `$match->captures`는 각 메타변수 이름을 캡처된 노드 또는 노드 목록에 매핑합니다.

## 작동 방식

파이프라인은 다음과 같습니다:

1. **패턴 파싱**: `Greph\Ast\PatternParser`은 식별자 메타변수를 다시 작성한 다음 PHP-Parser로 패턴을 파싱합니다.
2. **소스 파싱**: 각 후보 파일을 한 번 파싱합니다. `ast-cache` 모드에서는 이 단계가 캐시 저장소의 역직렬화로 대체됩니다.
3. **후보 필터링**: `Greph\Ast\AstPatternPrefilter`은 패턴에서 대략적인 시그니처 (호출 이름, 클래스 이름, 문장 유형)를 추출해 매치가 있을 수 없는 파일을 건너뜁니다. `ast-index` 모드에서는 미리 계산된 사실 저장소를 대신 조회합니다.
4. **매칭**: `Greph\Ast\PatternMatcher`은 소스 AST를 순회하며 패턴 AST와 비교하고, 메타변수를 바인딩하면서 반복 변수의 일관성을 확인합니다.
5. **결과 출력**: 매치된 노드는 줄 및 바이트 위치와 원본 소스 일부가 포함된 `AstMatch` 객체로 변환됩니다.

반복 워크로드에서는 [인덱싱된 AST 검색](https://greph.inline0.dev/ko/docs/indexed/ast)과 [캐시된 AST 검색](https://greph.inline0.dev/ko/docs/indexed/cache)이 2단계와 3단계의 대부분을 건너뜁니다.
