PHPackages                             greph/greph - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. greph/greph

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

greph/greph
===========

Pure PHP code search, structural search, and rewrite engine. grep, ripgrep, and ast-grep workflows in a single Composer package.

v0.2.2(3w ago)0335↓100%1MITPHPPHP ^8.2CI passing

Since Apr 11Pushed 3w agoCompare

[ Source](https://github.com/inline0/greph)[ Packagist](https://packagist.org/packages/greph/greph)[ Docs](https://github.com/inline0/greph)[ RSS](/packages/greph-greph/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (5)Versions (9)Used By (1)

    ![Greph](./docs/public/logo-light.svg)

 Pure PHP code search, structural search, and rewrite engine

 [![CI](https://github.com/inline0/greph/actions/workflows/ci.yml/badge.svg)](https://github.com/inline0/greph/actions/workflows/ci.yml) [![Packagist](https://camo.githubusercontent.com/5ef181e7b9e997fcfcdfd8d04c5a4bf1ddef1d10ff01aa6c3130ca3a7a8ffdde/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f67726570682f67726570682e737667)](https://packagist.org/packages/greph/greph) [![license](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](https://github.com/inline0/greph/blob/main/LICENSE)

---

What is Greph?
--------------

[](#what-is-greph)

Greph is a pure PHP search and refactoring engine for source trees. It covers three related modes in a single Composer package: text search, PHP AST search, and PHP AST rewrite. It also ships warmed indexed modes for both text and AST workloads, plus `rg` and `sg` compatibility wrappers for tool and agent environments.

**The problem:** PHP applications and agents that need fast code search usually shell out to native tools (`grep`, `ripgrep`, `ast-grep`), giving up portability and adding deployment friction. Structural search and rewrite is even worse: it normally means leaving the PHP process entirely.

**Greph solves this** by keeping the engine in PHP while still exposing familiar interfaces:

- Native text search and structural AST search backed by `nikic/php-parser`
- Format-preserving AST rewrite with dry-run, interactive, and write modes
- Warmed trigram + identifier text indexes for repeated workloads
- Warmed AST fact index and cached AST search for repeated structural queries
- ripgrep and ast-grep CLI compatibility wrappers backed by the same engine
- pcntl-based parallel worker pool with single-process fallback

Quick Start
-----------

[](#quick-start)

```
composer require greph/greph
```

```
# Text search
vendor/bin/greph -F "function" src

# Structural search
vendor/bin/greph -p 'new $CLASS()' src

# Structural rewrite preview
vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --dry-run src

# ripgrep-style compatibility
vendor/bin/rg --json -F "function" src

# ast-grep-style compatibility
vendor/bin/sg --pattern 'new $CLASS()' src --lang php

# Warm text index
vendor/bin/greph-index build .
vendor/bin/greph-index search -F "function" .

# Warm AST index / cache
vendor/bin/greph-index ast-index build .
vendor/bin/greph-index ast-index search 'new $CLASS()' src
vendor/bin/greph-index ast-cache build .
vendor/bin/greph-index ast-cache search 'array($$$ITEMS)' src
```

PHP API
-------

[](#php-api)

```
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;
use Greph\Text\TextSearchOptions;

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

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

// AST rewrite (returns the rewritten contents; the caller decides whether to write)
$rewrites = Greph::rewriteAst(
    'array($$$ITEMS)',
    '[$$$ITEMS]',
    'src',
);

// Indexed text
Greph::buildTextIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);
$results = Greph::searchTextIndexed('function', 'src');
$results = Greph::searchTextIndexedMany('function', '.', [
    'core/.greph-index',
    'plugins/demo/.greph-index',
]);
$results = Greph::searchTextIndexedSet(
    'function',
    '.',
    new TextSearchOptions(fixedString: true),
    '.greph-index-set.json',
    ['core-text', 'plugin-text'],
);

// Indexed AST (fact index)
Greph::buildAstIndex('.');
$matches = Greph::searchAstIndexed('new $CLASS()', 'src');
$matches = Greph::searchAstIndexedMany('new $CLASS()', '.', [
    'core/.greph-ast-index',
    'plugins/demo/.greph-ast-index',
]);
$matches = Greph::searchAstIndexedSet(
    'new $CLASS()',
    '.',
    new AstSearchOptions(),
    '.greph-index-set.json',
);

// Cached AST (parsed-tree cache)
Greph::buildAstCache('.');
$matches = Greph::searchAstCached('array($$$ITEMS)', 'src');
$matches = Greph::searchAstCachedMany('array($$$ITEMS)', '.', [
    'core/.greph-ast-cache',
    'plugins/demo/.greph-ast-cache',
]);
$matches = Greph::searchAstCachedSet(
    'array($$$ITEMS)',
    '.',
    new AstSearchOptions(),
    '.greph-index-set.json',
);
```

Process-restricted hosts
------------------------

[](#process-restricted-hosts)

The Greph engine runs in PHP. Optional oracle/support helpers that resolve external binaries or run compatibility commands can be constructed with an explicit process-disabled policy when embedding Greph in hosts where `proc_open()` / `shell_exec()` are unavailable or forbidden:

```
use Greph\Support\CommandRunner;
use Greph\Support\ToolResolver;

$toolResolver = ToolResolver::processDisabled();
$commandRunner = CommandRunner::processDisabled();
```

These helpers fail closed with clear errors instead of attempting host process lookup or execution. Native `Greph::search*()` and `Greph::rewrite*()` calls do not require them.

CLI
---

[](#cli)

Greph exposes four executables:

- `greph`: native text + AST search + 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

```
./vendor/bin/greph -F "function" src
./vendor/bin/greph -p 'new $CLASS()' src
./vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' src

./vendor/bin/greph-index build .
./vendor/bin/greph-index build . --lifecycle opportunistic-refresh
./vendor/bin/greph-index search -F "function" .
./vendor/bin/greph-index search -F "function" . --index-dir core/.greph-index --index-dir plugins/demo/.greph-index
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --dry-refresh
./vendor/bin/greph-index set search --show-index-origin -F "function" .
./vendor/bin/greph-index ast-index build .
./vendor/bin/greph-index ast-index search 'new $CLASS()' src
./vendor/bin/greph-index ast-index search 'new $CLASS()' . --index-dir core/.greph-ast-index --index-dir plugins/demo/.greph-ast-index
./vendor/bin/greph-index set search --mode ast-index 'new $CLASS()' .
./vendor/bin/greph-index ast-cache build .
./vendor/bin/greph-index ast-cache search 'array($$$ITEMS)' src
./vendor/bin/greph-index set search --mode ast-cache 'array($$$ITEMS)' .

./vendor/bin/rg -F "function" src
./vendor/bin/rg --files src

./vendor/bin/sg run --pattern 'new $CLASS()' src
./vendor/bin/sg run --pattern 'array($$$ITEMS)' --rewrite '[$$$ITEMS]' src
```

The compatibility wrappers are intentionally probe-driven rather than hand-waved. See [FEATURE\_MATRIX.md](FEATURE_MATRIX.md) for the current supported surface and the raw evidence in [FEATURE\_MATRIX.json](FEATURE_MATRIX.json).

Warm Index Lifecycle
--------------------

[](#warm-index-lifecycle)

Greph stays daemon-free. Warm indexes live on disk and are reused across runs.

Lifecycle profiles:

- `static`: never freshness-check or mutate automatically
- `manual-refresh`: inspect freshness in `stats`, never auto-refresh during search
- `opportunistic-refresh`: refresh on search only when the changed set is cheap
- `strict-stale-check`: refuse stale indexed searches

This makes mixed sets practical without a daemon:

- keep a stable `static` core index around
- keep plugin or app indexes as `opportunistic-refresh`
- let strict workloads fail hard with `strict-stale-check`

Examples:

```
./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 ast-cache build . --lifecycle strict-stale-check
./vendor/bin/greph-index stats . --dry-refresh
```

`greph-index stats` reports lifecycle, stale status, change summary, query-cache usage, and optional dry-run search behavior for text, AST index, and AST cache stores.

Planner Diagnostics
-------------------

[](#planner-diagnostics)

Warm indexed search can emit the actual planner decision to `stderr` without changing the user-facing result stream.

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

The trace includes:

- selected file count
- candidate source
- postings term count for warmed text plans
- candidate and verified file counts
- cache eligibility/population
- AST pattern root and cached-tree hit counts for warmed AST paths

Multi-Index Search
------------------

[](#multi-index-search)

Greph can search multiple warmed indexes in one request. This is intended for real layouts like WordPress core + plugin + theme, monorepos, or a static baseline plus a mutable overlay.

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

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

For repeated multi-index workflows, use a manifest instead of repeating flags:

```
{
  "name": "wordpress-local",
  "indexes": [
    {
      "name": "core-text",
      "root": "wordpress",
      "mode": "text",
      "lifecycle": "static",
      "priority": 100
    },
    {
      "name": "plugin-text",
      "root": "wp-content/plugins/my-plugin",
      "mode": "text",
      "lifecycle": "opportunistic-refresh",
      "max_changed_files": 16,
      "max_changed_bytes": 262144,
      "priority": 200
    },
    {
      "name": "plugin-ast",
      "root": "wp-content/plugins/my-plugin",
      "mode": "ast-index",
      "priority": 200
    },
    {
      "name": "plugin-cache",
      "root": "wp-content/plugins/my-plugin",
      "mode": "ast-cache",
      "priority": 200
    }
  ]
}
```

Save that as `.greph-index-set.json`, then:

```
./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)' .
```

Useful WordPress shape:

- `wordpress` text index: `static`
- `wordpress` AST index: `static`
- plugin text/AST indexes: `opportunistic-refresh`
- theme text/AST indexes: `opportunistic-refresh`

That keeps warmed baseline indexes stable while letting active overlays refresh cheaply when a command runs.

Documentation
-------------

[](#documentation)

The repo includes a dedicated docs app under [`docs/`](docs) that mirrors the same release/docs structure used in sibling projects.

```
cd docs
npm install
npm run dev
```

Topics covered:

- Getting started, CLI reference, and PHP API
- Text mode, AST mode, and rewrite mode
- Indexed text, indexed AST, and cached AST
- `rg` and `sg` compatibility wrappers
- File walker, parallel workers, feature matrix, testing, and benchmarks

Testing
-------

[](#testing)

Greph is validated with unit tests, an oracle-style regression corpus, and a probe-driven feature matrix.

```
# PHPUnit unit + integration tests
composer test

# Oracle regression corpus (text, AST, rewrite)
composer test:oracle

# Static analysis
composer analyse

# Coding standards
composer cs

# Full release-grade verification (analyse + cs + test + test:oracle)
composer verify

# Refresh the generated feature matrix
php bin/feature-matrix
```

The oracle harness diffs Greph output against the canonical `grep`, `ripgrep`, and `ast-grep` binaries on a fixed scenario set. Scenarios live under [`scenarios/`](scenarios), the runner lives under [`tests/Oracle/`](tests/Oracle), and the verified compatibility surface lives in [FEATURE\_MATRIX.md](FEATURE_MATRIX.md).

Current local verification baseline:

- `252` PHPUnit tests
- `1366` assertions
- oracle regression summary `39/39`

Performance
-----------

[](#performance)

Benchmark tables below are sourced from GitHub Actions CI, never from local runs. The current published baseline comes from GitHub Actions run `24342599916`, against the WordPress corpus on `ubuntu-latest` with PHP `8.4`, five measured runs, and one warmup run.

Comparison tools:

- `rg`: ripgrep
- `grep`: GNU grep
- `sg`: ast-grep

### Scan Mode: Text

[](#scan-mode-text)

Cold scan mode optimizes for one-shot searches without any prebuilt index. It is the baseline path for ad hoc text queries over a tree.

Operationgrephrggrep`Literal "function"``458.82ms``81.62ms``174.79ms``Literal case insensitive``448.42ms``83.65ms``238.35ms``Literal quiet "function"``255.60ms``3.24ms``1.19ms``Literal short "wp"``438.63ms``98.22ms``153.09ms``Literal whole word``703.06ms``81.54ms``177.84ms``Regex new instance``459.58ms``36.76ms``155.48ms``Regex array call``399.86ms``44.81ms``177.25ms``Regex prefix literal``439.99ms``36.72ms``154.77ms``Regex suffix literal``583.73ms``124.62ms``224.11ms``Regex exact line literal``549.01ms``72.48ms``158.47ms``Regex literal collapse``439.21ms``81.90ms``172.53ms`### Scan Mode: Traversal

[](#scan-mode-traversal)

Traversal isolates walker and ignore-rule overhead without the text matcher dominating the result.

Operationgrephrggrep`Full traversal``44.98ms``12.36ms``44.48ms`### Scan Mode: Parallel Text

[](#scan-mode-parallel-text)

Parallel text shows whether worker fan-out helps on real one-shot scan workloads after traversal and matcher costs are included.

Operationgrephrggrep`1 worker``439.83ms``81.72ms``180.08ms``2 workers``426.52ms``83.31ms``179.49ms``4 workers``441.82ms``83.23ms``182.74ms`### Scan Mode: AST

[](#scan-mode-ast)

Cold AST mode optimizes for one-shot structural search without a prebuilt fact index or cached parse store.

Operationgrephsg`new $CLASS()``2859.27ms``4090.82ms``array($$$ITEMS)``5537.70ms``4133.41ms`### Indexed Text Mode

[](#indexed-text-mode)

Indexed text mode optimizes for repeated text queries on the same tree, where warm postings and query caches can avoid most rescanning.

Operationgrephrggrep`Indexed literal "function"``90.50ms``86.12ms``173.60ms``Indexed literal case insensitive``98.94ms``92.68ms``250.24ms``Indexed literal short "wp"``124.91ms``108.16ms``151.26ms``Indexed literal whole word``92.73ms``86.45ms``176.18ms``Indexed regex new instance``10.46ms``37.67ms``166.84ms``Indexed regex array call``26.24ms``44.46ms``174.53ms`### Indexed Summary Queries

[](#indexed-summary-queries)

Indexed summary queries optimize for count, filename, and quiet/existence answers that can often be served directly from warmed postings.

Operationgrephrggrep`Indexed count "function"``10.90ms``49.61ms``148.46ms``Indexed files with "function"``10.00ms``45.57ms``95.96ms``Indexed files without "function"``9.74ms``81.56ms``96.10ms``Indexed quiet "function"``5.50ms``3.40ms``1.18ms`### Indexed / Cached AST

[](#indexed--cached-ast)

Indexed and cached AST modes optimize for repeated structural PHP queries by reusing file-level facts or whole parsed trees instead of reparsing the repository.

Operationgrephsg`Indexed new $CLASS()``10.89ms``4093.61ms``Indexed array($$$ITEMS)``324.79ms``4149.52ms``Cached new $CLASS()``13.03ms``4096.84ms``Cached array($$$ITEMS)``314.29ms``4146.10ms`### Build Costs

[](#build-costs)

Build costs measure the upfront price of preparing warmed indexes and caches that later queries reuse.

Operationgreph`Build trigram index``12306.51ms``Build AST fact index``1354.98ms``Build cached AST store``10148.88ms`Requirements
------------

[](#requirements)

- PHP 8.2+
- `ext-json` (built-in on virtually every PHP install)
- `nikic/php-parser` ^5.7 (the only Composer dependency)

Optional:

- `ext-pcntl` for parallel worker scans. Greph degrades gracefully to single-process execution when missing.
- External `rg`, `grep`, and `sg` binaries for benchmark comparisons. Greph itself does not require them at runtime.

Features
--------

[](#features)

CategoryFeaturesText Searchfixed-string, regex, case-insensitive, whole-word, context, counts, file-only modesAST SearchPHP structural search with captures, repeated metavariables, variadics, JSON outputRewritedry-run, interactive, write mode, format-preserving, captured-variable splicingIndexed Textwarmed trigram + identifier postings, summary queries, cold/warm benchmark coverageIndexed ASTfact-backed search and cached parsed-tree searchCompatibility`rg` and `sg` wrappers backed by the same engine, probe-verified against the upstream binariesValidationprobe-driven feature matrix, oracle regressions, CI benchmarks, 100% `src/` coverageArchitecture
------------

[](#architecture)

```
src/
├── Greph.php                  # Static facade (open, search, rewrite, index)
├── Cli/                       # greph, greph-index, rg, sg frontends
├── Text/                      # grep-style search engine
├── Ast/                       # PHP AST search and rewrite engine
├── Index/                     # warmed text / AST indexes and caches
├── Walker/                    # filesystem traversal and ignore rules
├── Parallel/                  # worker pool and result collection
├── FeatureMatrix/             # generated compatibility probes
├── Output/                    # grep-style formatting
├── Support/                   # filesystem, command, JSON, tool helpers
└── Exceptions/                # typed exceptions

```

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance95

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~12 days

Total

4

Last Release

21d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/dc81a975082b6843421de211768cfb67d8237bed4c972c9a1eaad46aac14b372?d=identicon)[dnnsjsk](/maintainers/dnnsjsk)

---

Top Contributors

[![dnnsjsk](https://avatars.githubusercontent.com/u/73965001?v=4)](https://github.com/dnnsjsk "dnnsjsk (158 commits)")

---

Tags

phprefactoringastrewritegrepindexed-searchcode-searchripgrepast-grepstructural-search

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/greph-greph/health.svg)

```
[![Health](https://phpackages.com/badges/greph-greph/health.svg)](https://phpackages.com/packages/greph-greph)
```

###  Alternatives

[ajthinking/archetype

Programmatically edit PHP and Laravel files.

2723.7M17](/packages/ajthinking-archetype)[atanamo/php-codeshift

A PHP code transformation toolkit based on 'PHP-Parser'

32159.6k1](/packages/atanamo-php-codeshift)[corveda/php-sandbox

A PHP library that can be used to run PHP code in a sandboxed environment

23990.9k2](/packages/corveda-php-sandbox)[voku/simple-php-code-parser

Get a simple data structure from your php code.

487.3k5](/packages/voku-simple-php-code-parser)[butschster/proto-parser

Proto parser is a library for parsing Protocol Buffers files into AST

581.2k](/packages/butschster-proto-parser)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
