
# PHP API

## `Greph\Greph`

Die Klasse `Greph\Greph` ist der statische Einstiegspunkt. Sie bietet alle Such-, Rewrite- und Indexoperationen. Alle Methoden akzeptieren entweder einen einzelnen Pfad oder eine Liste von Pfaden.

```php
use Greph\Greph;
```

### `walk`

```php
public static function walk(string|array $paths, ?WalkOptions $options = null): FileList
```

Löse `$paths` in eine flache `FileList` aus absoluten Dateipfaden auf. Berücksichtigt gitignore, `.grephignore`, Dateitypfilter, Glob-Muster, das Einbeziehen versteckter Dateien, das Folgen symbolischer Links, die Binärerkennung und die maximale Dateigröße.

```php
use Greph\Walker\WalkOptions;
use Greph\Walker\FileTypeFilter;

$files = Greph::walk('src', new WalkOptions(
    fileTypeFilter: new FileTypeFilter(['php']),
));

foreach ($files->paths() as $path) {
    echo "$path\n";
}
```

---

### `searchText`

```php
public static function searchText(
    string $pattern,
    string|array $paths,
    ?TextSearchOptions $options = null,
): array
```

Führe eine Textsuche aus und gib ein `list<TextFileResult>` zurück. Jedes `TextFileResult` enthält den Dateipfad und eine Liste von `TextMatch`-Objekten (Zeile, Spalte, Inhalt, Treffer und optionaler Kontext).

```php
use Greph\Greph;
use Greph\Text\TextSearchOptions;

$results = Greph::searchText(
    'function',
    'src',
    new TextSearchOptions(
        fixedString: true,
        caseInsensitive: true,
        beforeContext: 1,
        afterContext: 1,
        jobs: 4,
    ),
);

foreach ($results as $file) {
    foreach ($file->matches as $match) {
        echo "{$file->file}:{$match->line}:{$match->content}\n";
    }
}
```

Wenn `jobs > 1` gilt und die Dateianzahl den Worker-Schwellenwert überschreitet, verteilt `searchText` den Scan automatisch auf ein `WorkerPool`. Andernfalls wird er im selben Prozess ausgeführt.

---

### `searchAst`

```php
public static function searchAst(
    string $pattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
): array
```

Führe eine strukturelle AST-Suche aus und gib ein `list<AstMatch>` zurück. Muster werden als PHP-Quelltext mit Metavariablen `$VAR` und `$$$VARIADIC` geschrieben. Jedes `AstMatch` enthält die Datei, das erfasste `PhpParser\Node`, die Treffer, die Start- und Endzeile sowie Byte-Offsets und den übereinstimmenden Quelltextausschnitt.

```php
use Greph\Greph;
use Greph\Ast\AstSearchOptions;

$matches = Greph::searchAst(
    'new $CLASS()',
    'src',
    new AstSearchOptions(),
);

foreach ($matches as $match) {
    echo "{$match->file}:{$match->startLine}:{$match->code}\n";
}
```

---

### `rewriteAst`

```php
public static function rewriteAst(
    string $searchPattern,
    string $rewritePattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
): array
```

Führe ein strukturelles Rewrite aus und gib ein `list<RewriteResult>` zurück. Jedes `RewriteResult` enthält den Dateipfad, den ursprünglichen Inhalt, den umgeschriebenen Inhalt und die Anzahl der Ersetzungen. Das Rewrite bewahrt die Formatierung: Nur die Bereiche der übereinstimmenden Knoten werden ersetzt; der Rest der Datei bleibt unverändert.

```php
$rewrites = Greph::rewriteAst(
    'array($$$ITEMS)',
    '[$$$ITEMS]',
    'src',
);

foreach ($rewrites as $result) {
    if ($result->changed()) {
        file_put_contents($result->file, $result->rewrittenContents);
    }
}
```

Die Fassade schreibt nicht auf die Festplatte; du entscheidest, ob die Änderungen übernommen werden. CLI übernimmt sie, sofern `--dry-run` oder `--interactive` nicht gesetzt ist.

---

### Indizierte Textsuche

#### `buildTextIndex`

```php
public static function buildTextIndex(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): IndexBuildResult
```

Erstelle einen neuen Trigramm- und Bezeichner-Posting-Index für `$rootPath`. Der Index wird unter `$indexPath` gespeichert (Standardwert: `$rootPath/.greph-index`). `$lifecycle` kann `static`, `manual-refresh`, `opportunistic-refresh` oder `strict-stale-check` sein. Gibt ein `IndexBuildResult` mit der Dateianzahl, der Trigramm-Anzahl sowie der Anzahl hinzugefügter, aktualisierter, gelöschter und unveränderter Einträge zurück.

#### `refreshTextIndex`

```php
public static function refreshTextIndex(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): IndexBuildResult
```

Aktualisiere einen vorhandenen Textindex schrittweise. Dateien werden neu gehasht, und nur geänderte Einträge werden neu geschrieben.

#### `searchTextIndexed`

```php
public static function searchTextIndexed(
    string $pattern,
    string|array $paths,
    ?TextSearchOptions $options = null,
    ?string $indexPath = null,
): array
```

Führe eine Textsuche im vorbereiteten Index aus. Falls der Index nicht existiert, wird ein Laufzeitfehler ausgelöst (rufe zuerst `buildTextIndex` auf). Gibt ein `list<TextFileResult>` zurück, dessen Struktur exakt `searchText` entspricht.

```php
Greph::buildTextIndex('.');
$results = Greph::searchTextIndexed('function', 'src');
```

#### `searchTextIndexedMany`

```php
public static function searchTextIndexedMany(
    string $pattern,
    string|array $paths,
    array $indexPaths,
    ?TextSearchOptions $options = null,
): array
```

Führe eine Textsuche in mehreren vorbereiteten Textindexverzeichnissen aus. Greph durchsucht jeden passenden Index, entfernt Duplikate anhand des absoluten Dateipfads und greift nur für Pfade außerhalb aller angegebenen Indexstammverzeichnisse auf den nativen Scan zurück.

#### `loadIndexSet`

```php
public static function loadIndexSet(?string $manifestPath = null): IndexSet
```

Lade ein Indexsatzmanifest; standardmäßig `.greph-index-set.json` im aktuellen Arbeitsverzeichnis.

#### `searchTextIndexedSet`

```php
public static function searchTextIndexedSet(
    string $pattern,
    string|array $paths,
    ?TextSearchOptions $options = null,
    ?string $manifestPath = null,
    array $entryNames = [],
): array
```

Löse die ausgewählten `text`-Einträge aus einem Indexsatzmanifest auf und führe das Äquivalent von `searchTextIndexedMany` aus.

---

### Indizierte AST-Suche

#### `buildAstIndex` / `refreshAstIndex`

```php
public static function buildAstIndex(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): AstIndexBuildResult
public static function refreshAstIndex(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): AstIndexBuildResult
```

Erstelle einen AST-Faktenindex oder aktualisiere ihn schrittweise. Extrahierte Fakten werden standardmäßig unter `$rootPath/.greph-ast-index` gespeichert.

#### `searchAstIndexed`

```php
public static function searchAstIndexed(
    string $pattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
    ?string $indexPath = null,
): array
```

Verwende den Faktenindex, um die Kandidaten einzugrenzen, bevor Treffer erneut anhand des Quelltexts geprüft werden. Gibt dieselbe `list<AstMatch>`-Struktur wie `searchAst` zurück.

#### `searchAstIndexedMany`

```php
public static function searchAstIndexedMany(
    string $pattern,
    string|array $paths,
    array $indexPaths,
    ?AstSearchOptions $options = null,
): array
```

Führe eine vorbereitete AST-Faktensuche in mehreren AST-Indexverzeichnissen aus.

#### `searchAstIndexedSet`

```php
public static function searchAstIndexedSet(
    string $pattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
    ?string $manifestPath = null,
    array $entryNames = [],
): array
```

Löse die ausgewählten `ast-index`-Einträge aus einem Indexsatzmanifest auf und führe das Äquivalent von `searchAstIndexedMany` aus.

#### `buildAstCache` / `refreshAstCache`

```php
public static function buildAstCache(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): AstCacheBuildResult
public static function refreshAstCache(
    string $rootPath = '.',
    ?string $indexPath = null,
    IndexLifecycle|IndexLifecycleProfile|string|null $lifecycle = null,
): AstCacheBuildResult
```

Erstelle einen Cache für geparste Syntaxbäume oder aktualisiere ihn schrittweise. Serialisierte Bäume werden standardmäßig unter `$rootPath/.greph-ast-cache` gespeichert.

#### `searchAstCached`

```php
public static function searchAstCached(
    string $pattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
    ?string $indexPath = null,
): array
```

Führe eine AST-Suche anhand der zwischengespeicherten Syntaxbäume aus und überspringe den Parser vollständig. Gibt dieselbe `list<AstMatch>`-Struktur wie `searchAst` zurück.

#### `searchAstCachedMany`

```php
public static function searchAstCachedMany(
    string $pattern,
    string|array $paths,
    array $indexPaths,
    ?AstSearchOptions $options = null,
): array
```

Führe eine vorbereitete Suche im zwischengespeicherten AST in mehreren Cache-Verzeichnissen aus.

#### `searchAstCachedSet`

```php
public static function searchAstCachedSet(
    string $pattern,
    string|array $paths,
    ?AstSearchOptions $options = null,
    ?string $manifestPath = null,
    array $entryNames = [],
): array
```

Löse die ausgewählten `ast-cache`-Einträge aus einem Indexsatzmanifest auf und führe das Äquivalent von `searchAstCachedMany` aus.

---

## `Greph\Text\TextSearchOptions`

Schreibgeschütztes Optionsobjekt für die Textsuche.

```php
new TextSearchOptions(
    bool $fixedString = false,
    bool $caseInsensitive = false,
    bool $wholeWord = false,
    bool $invertMatch = false,
    ?int $maxCount = null,
    int $beforeContext = 0,
    int $afterContext = 0,
    bool $countOnly = false,
    bool $filesWithMatches = false,
    bool $filesWithoutMatches = false,
    bool $jsonOutput = false,
    bool $tracePlan = false,
    int $jobs = 1,
    bool $respectIgnore = true,
    bool $includeHidden = false,
    bool $followSymlinks = false,
    bool $skipBinaryFiles = true,
    bool $includeGitDirectory = false,
    ?FileTypeFilter $fileTypeFilter = null,
    int $maxFileSizeBytes = 10485760,
    array $globPatterns = [],
    bool $showLineNumbers = true,
    bool $showFileNames = true,
)
```

Validierung:

- `jobs >= 1`
- `beforeContext, afterContext >= 0`
- `maxCount === null || maxCount >= 1`

Verwende `walkOptions()`, um das passende `WalkOptions` abzuleiten, wenn du den Dateibaum separat durchlaufen möchtest.

`tracePlan` wird von vorbereiteten indizierten Suchwerkzeugen verwendet, und `greph-index` CLI leitet Abläufe weiter, um Planungsdiagnosen an `stderr` auszugeben. Die native `searchText` selbst verwendet es nicht.

---

## `Greph\Ast\AstSearchOptions`

Schreibgeschütztes Optionsobjekt für die AST-Suche und Rewrite.

```php
new AstSearchOptions(
    string $language = 'php',
    int $jobs = 1,
    bool $respectIgnore = true,
    bool $includeHidden = false,
    bool $followSymlinks = false,
    bool $skipBinaryFiles = true,
    bool $includeGitDirectory = false,
    ?FileTypeFilter $fileTypeFilter = null,
    int $maxFileSizeBytes = 10485760,
    array $globPatterns = [],
    bool $skipParseErrors = true,
    bool $dryRun = false,
    bool $interactive = false,
    bool $jsonOutput = false,
    bool $tracePlan = false,
)
```

Der Standard-Dateitypfilter für den AST-Modus ist `php`. Wenn `skipParseErrors` den Wert `true` hat (Standardwert), werden Dateien, die sich nicht parsen lassen, stillschweigend übersprungen. Setze den Wert auf `false`, um Parse-Fehler als Ausnahmen auszugeben. `tracePlan` wird von vorbereiteten AST-Suchwerkzeugen und `greph-index ast-index|ast-cache search` verwendet, um Diagnosen zur Kandidatenfilterung auszugeben.

---

## `Greph\Walker\WalkOptions`

Schreibgeschütztes Optionsobjekt für den Datei-Walker.

```php
new WalkOptions(
    bool $respectIgnore = true,
    bool $includeHidden = false,
    bool $followSymlinks = false,
    bool $skipBinaryFiles = false,
    bool $includeGitDirectory = false,
    ?FileTypeFilter $fileTypeFilter = null,
    int $maxFileSizeBytes = 10485760,
    array $globPatterns = [],
)
```

---

## `Greph\Walker\FileTypeFilter`

Löse benannte Dateitypen in Erweiterungslisten auf. Integrierte Aliase:

| Alias | Erweiterungen |
|---|---|
| `css` | `css`, `sass`, `scss` |
| `html` | `htm`, `html`, `phtml` |
| `js` | `cjs`, `js`, `mjs` |
| `json` | `json` |
| `md` | `markdown`, `md` |
| `php` | `inc`, `php`, `php3`, `php4`, `php5`, `php7`, `php8`, `phpt`, `phtml` |
| `txt` | `txt` |
| `ts` | `ts`, `tsx` |
| `xml` | `xml` |
| `yaml` | `yaml`, `yml` |

```php
new FileTypeFilter(includeTypes: ['php'], excludeTypes: ['md']);
```

---

## Ergebnisobjekte

### `Greph\Text\TextFileResult`

```php
final readonly class TextFileResult
{
    public string $file;
    /** @var list<TextMatch> */
    public array $matches;

    public function matchCount(): int;
    public function hasMatches(): bool;
}
```

### `Greph\Text\TextMatch`

```php
final readonly class TextMatch
{
    public string $file;
    public int $line;
    public int $column;
    public string $content;
    public string $matchedText;
    /** @var array<int|string, string> */
    public array $captures;
    /** @var list<array{line: int, content: string}> */
    public array $beforeContext;
    /** @var list<array{line: int, content: string}> */
    public array $afterContext;
}
```

### `Greph\Ast\AstMatch`

```php
final readonly class AstMatch
{
    public string $file;
    public PhpParser\Node $node;
    /** @var array<string, mixed> */
    public array $captures;
    public int $startLine;
    public int $endLine;
    public int $startFilePos;
    public int $endFilePos;
    public string $code;
}
```

### `Greph\Ast\RewriteResult`

```php
final readonly class RewriteResult
{
    public string $file;
    public string $originalContents;
    public string $rewrittenContents;
    public int $replacementCount;

    public function changed(): bool;
}
```

### `Greph\Index\IndexBuildResult`

```php
final readonly class IndexBuildResult
{
    public string $rootPath;
    public string $indexPath;
    public int $fileCount;
    public int $trigramCount;
    public int $addedFiles;
    public int $updatedFiles;
    public int $deletedFiles;
    public int $unchangedFiles;
}
```

`AstIndexBuildResult` und `AstCacheBuildResult` haben dieselbe Struktur; dabei werden jeweils `factCount` und `cachedTreeCount` eingesetzt.

---

## Ausnahmen

Greph löst drei typisierte Ausnahmen aus, alle in `Greph\Exceptions`:

| Ausnahme | Wann sie ausgelöst wird |
|---|---|
| `WalkerException` | Dateisystemfehler beim Durchlaufen (fehlender Pfad, nicht lesbares Verzeichnis) |
| `PatternException` | Ungültiger Regex- oder AST-Ausdruck |
| `ParseException` | PHP-Quelle konnte nicht geparst werden (nur wenn `skipParseErrors: false`) |

Indexierte Suchmethoden lösen `\RuntimeException` aus, wenn am angeforderten Speicherort kein Index vorhanden ist. Die `greph-index` CLI bietet eine Option `--fallback scan`, die diese Fehler abfängt und stattdessen auf eine Suche ohne Index zurückgreift.
