
# Getting Started

## Installation

Install Greph via Composer:

```bash
composer require greph/greph
```

This installs the library plus four executables in `vendor/bin/`:

- `greph`: native text search, AST search, and AST rewrite
- `greph-index`: warmed text index, AST fact index, and cached AST search
- `rg`: ripgrep-style compatibility wrapper
- `sg`: ast-grep-style compatibility wrapper

These executables work from your project's `vendor/bin/` since Greph 0.2.3. Earlier releases only found the Composer autoloader inside a clone of the Greph repository, so require at least that version:

```bash
composer require greph/greph:^0.2.3
```

## Requirements

- **PHP 8.2+** for readonly classes, enums, first-class callable syntax, and constructor promotion
- **ext-json** for JSON output and on-disk index payloads
- **ext-ctype** for pattern and option parsing
- **ext-tokenizer** for PHP-Parser and the AST prefilters
- **ext-zlib** for the cached AST store (`greph-index ast-cache`)
- **nikic/php-parser** (installed automatically as a Composer dependency)

`ext-json`, `ext-ctype` and `ext-tokenizer` are enabled in almost every PHP build. `composer.json` declares only `ext-json` at the moment ([#5](https://github.com/inline0/greph/issues/5)), so Composer does not warn when one of the others is missing.

Optional:

- **ext-pcntl** for parallel scans. Greph degrades gracefully to single-process execution when it is missing.
- External `rg`, `grep`, and `sg` binaries for benchmark comparisons. They are never required at runtime.

Searching, rewriting and indexing never call `proc_open()` or `shell_exec()`; only parallel scans fork, and they fall back to a single process without `pcntl_fork()`. See [Process-Restricted Hosts](/docs/advanced/process-restricted-hosts) for hosts that disable `proc_open()`, `shell_exec()` or `pcntl_fork()`.

## Your first text search

Greph's default mode is grep-compatible text search:

```bash
vendor/bin/greph -F "function" src
```

The output uses the canonical `file:line:content` format that grep, ripgrep, and most editor integrations expect.

```text
src/Greph.php:41:    public static function walk(string|array $paths, ?WalkOptions $options = null): FileList
src/Greph.php:50:    public static function searchText(string $pattern, string|array $paths, ?TextSearchOptions $options = null): array
src/Greph.php:81:    public static function buildTextIndex(
```

Add `-i` for case-insensitive matching, `-w` for whole-word matching, `-c` for counts, `-l` for file paths only, `-C 2` for two lines of context, and so on. The full list lives in the [CLI reference](/docs/cli).

## Your first AST search

Pass `-p` and a PHP-shaped pattern to switch to structural search:

```bash
vendor/bin/greph -p 'new $CLASS()' src
```

`$CLASS` is a metavariable that captures any class name. The pattern `new $CLASS()` matches every `new` expression without arguments, regardless of class, formatting, or comments. Use `new $CLASS($$$ARGS)` to match calls with any arguments.

Each AST match is printed as the file, the start line, and the matched code:

```text
src/Greph.php:43:new FileWalker()
src/Greph.php:52:new TextSearchOptions()
src/Greph.php:54:new TextSearcher()
```

Other useful patterns:

```bash
# Any method call with any arguments
vendor/bin/greph -p '$obj->$method($$$ARGS)' src

# Any function declaration with a void return type
vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src

# Old-style array calls
vendor/bin/greph -p 'array($$$ITEMS)' src
```

See [Modes / AST Search](/docs/modes/ast) for the full pattern syntax.

## Your first rewrite

Add `-r` to turn the AST search into a rewrite:

```bash
# Preview only
vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --dry-run src

# Apply
vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' src

# Confirm each file before writing
vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --interactive src
```

Rewrites are format-preserving outside the match: only the matched node is replaced, and the surrounding whitespace, indentation, and comments stay intact. The matched node itself is printed fresh from the template, so its own layout is normalized and a comment inside it can be lost. See [Modes / Rewrite](/docs/modes/rewrite) for details.

## PHP API

The static `Greph\Greph` facade exposes the same operations as the CLI:

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

$results = Greph::searchText(
    'function',
    'src',
    new TextSearchOptions(fixedString: true, caseInsensitive: true),
);

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

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

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

The full surface is covered in [API](/docs/api).

## Indexed search

Repeated workloads benefit from warmed indexes. Build once, then query many times:

```bash
# Text index
vendor/bin/greph-index build .
vendor/bin/greph-index search -F "function" .

# AST fact index
vendor/bin/greph-index ast-index build .
vendor/bin/greph-index ast-index search 'new $CLASS()' src

# Cached parsed-tree index
vendor/bin/greph-index ast-cache build .
vendor/bin/greph-index ast-cache search 'array($$$ITEMS)' src
```

Indexes live in `.greph-index`, `.greph-ast-index`, and `.greph-ast-cache` at the project root by default.

For actively changing trees, choose a lifecycle profile instead of treating every warmed index the same:

```bash
vendor/bin/greph-index build . --lifecycle static
vendor/bin/greph-index build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576
vendor/bin/greph-index stats . --dry-refresh
```

The common pattern is:

- keep a stable baseline as `static`
- keep actively changing overlays as `opportunistic-refresh`
- inspect stale behavior with `stats --dry-refresh`

### Multi-index warmed search

You can search several warmed indexes in one command:

```bash
vendor/bin/greph-index search -F "apply_filters" . \
  --index-dir wordpress/.greph-index \
  --index-dir wp-content/plugins/my-plugin/.greph-index \
  --show-index-origin

vendor/bin/greph-index ast-index search 'new $CLASS()' . \
  --index-dir wordpress/.greph-ast-index \
  --index-dir wp-content/plugins/my-plugin/.greph-ast-index \
  --show-index-origin
```

### Named index sets

If you do that repeatedly, put the layout into `.greph-index-set.json` and use the set subcommands instead of repeating flags:

```bash
vendor/bin/greph-index set build
vendor/bin/greph-index set stats --dry-refresh
vendor/bin/greph-index set search --show-index-origin -F "apply_filters" .
vendor/bin/greph-index set search --mode ast-index 'new $CLASS()' .
vendor/bin/greph-index set search --mode ast-cache 'array($$$ITEMS)' .
```

### Planner trace

To see how warmed search is narrowing candidates, add `--trace-plan`:

```bash
vendor/bin/greph-index search --trace-plan -F "function" .
vendor/bin/greph-index ast-index search --trace-plan 'new $CLASS()' .
```

See [Indexed / Text](/docs/indexed/text) and [Indexed / AST](/docs/indexed/ast) for the full warmed workflow.

## Compatibility wrappers

If you have tools or agents that already speak ripgrep or ast-grep, drop in the wrappers:

```bash
vendor/bin/rg --json -F "function" src
vendor/bin/sg --pattern 'new $CLASS()' src --lang php
```

Both wrappers reuse the same engine and are probe-verified against the upstream binaries. The verified surface is documented in [Compatibility / rg](/docs/compatibility/rg) and [Compatibility / sg](/docs/compatibility/sg) and reproduced in the generated [feature matrix](/docs/advanced/feature-matrix).

## Configuration

Greph picks up two on-disk conventions:

- `.gitignore`: respected by default. Pass `--no-ignore` to disable.
- `.grephignore`: same syntax as `.gitignore`, applied on top. Useful for tool-specific exclusions you do not want in version control.

The default index directories are `.greph-index`, `.greph-ast-index`, and `.greph-ast-cache` at the chosen root. Override with `--index-dir` on any `greph-index` subcommand, or use `.greph-index-set.json` for repeatable multi-index layouts.
