Greph
Pure PHP code search, structural search, and rewrite engine. grep, ripgrep, and ast-grep workflows in a single Composer package.
composer require greph/grephLatest release v0.2.3 MIT PHP 8.2+
# 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)' srcIn practice
Install the package with Composer, then search and rewrite from the command line or from PHP.
Search, match and preview a rewrite
-F runs a fixed-string text search, -p takes a PHP-shaped pattern with metavariables such as $CLASS, and -r adds a replacement template. --dry-run prints the rewritten files without writing them.
The static Greph facade exposes the same operations to PHP code. Installed as a dependency, the vendor/bin commands need version 0.2.3 or later.
# 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 srcuse Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Text\TextSearchOptions;
$matches = Greph::searchText(
'function',
'src',
new TextSearchOptions(fixedString: true),
);
$asts = Greph::searchAst(
'new $CLASS()',
'src',
new AstSearchOptions(),
);Pure PHP
Search and rewrite run in PHP 8.2+, with no grep, ripgrep or ast-grep binary. Uses the json, ctype and tokenizer extensions, plus zlib for the cached AST store.
Text Search
Fixed-string and regex search with whole-word, case-insensitive, context, count, and file-only modes. grep-compatible output.
AST Search
Structural search over PHP source with $VAR and $$$VARIADIC metavariables, repeated captures, and JSON output. PHP is the only supported language.
AST Rewrite
Structural rewrites with dry-run, interactive, and write modes. Only matched code changes; comments and layout outside a match stay as they were.
Indexed Modes
Build a trigram text index, an AST fact index or a cached AST store once and reuse it. In the project benchmark, the AST fact index took 1.4 s to build and cut new $CLASS() from 2.9 s to 11 ms; the trigram index took 12.3 s.
rg & sg Wrappers
vendor/bin/rg and vendor/bin/sg accept the common ripgrep and ast-grep search flags on the same engine. Not covered: multiline, --replace, --type-add and --sort in rg; YAML rules and non-PHP languages in sg.
PHP API
Greph::searchText(), searchAst() and rewriteAst() return typed result objects, so PHP code and agents call the engine without a subprocess.
Oracle Corpus
39 scenarios diff greph text, AST and rewrite output against grep, ripgrep and ast-grep. Run them with composer test:oracle in a clone of the repository; CI runs the unit and integration suites.
Why Greph
No native binaries
Search and rewrite run inside the PHP process. The rg, grep and sg binaries are only used for the optional benchmark comparisons.
One engine for every mode
Text search, PHP structural search and rewrite live in one Composer package. The rg and sg wrappers call the same engine as vendor/bin/greph.
Rewrites touch only matches
The rewriter splices the rendered template into the matched byte ranges. Code outside a match keeps its indentation, comments and blank lines.
Warm indexes without a daemon
Indexes live on disk and are reused across runs. A lifecycle profile decides whether a stale index is left alone, refreshed during search or refused.
Compared with
Greph keeps search and rewrite in PHP. These tools solve related problems and do some things Greph does not.
| Tool | Good at | Difference with Greph |
|---|---|---|
| ripgrep | Good atA recursive regex search tool with first class support on Windows, macOS and Linux and binary downloads for every release. It respects gitignore rules and skips hidden and binary files by default. | Difference with GrephGreph installs with Composer and runs in PHP. In its CI benchmark on the WordPress corpus, rg was faster on every cold text query, for example 81.62 ms against 458.82 ms for a literal search. A warm Greph index answered a count query in 10.90 ms against 49.61 ms for rg. |
| ast-grep | Good atA compiled CLI for structural search, lint and rewrite, built on tree-sitter. It has built-in support for many languages, PHP among them, and YAML rule files for lint rules and code changes. | Difference with GrephGreph handles PHP only and reads no YAML rules. On a cold scan sg was faster for array($$$ITEMS) in Greph's CI benchmark. Greph's warm AST index answered new $CLASS() in 10.89 ms against 4093.61 ms for sg. |
| Rector | Good atUpgrades and refactors PHP code through rules and rule sets, covering PHP versions from 5.3 to 8.5 and projects such as Symfony, PHPUnit and Doctrine. | Difference with GrephGreph rewrites from a pattern and a template you write. Its docs rule it out when a change depends on type information PHP-Parser cannot resolve from a single file, or needs imports added or removed across files. |
| PHP CS Fixer | Good atFixes code to follow coding standards, with built-in rule sets such as PHP-FIG's PER Coding Style. | Difference with GrephGreph does not reformat files. It changes only the code a pattern matched, and its docs point to PHP CS Fixer when you want a formatter. |
Sources, checked
Questions and answers
Short answers from the Greph documentation.
No. Greph runs search and rewrite in PHP. The external rg, grep and sg binaries are only used for the optional benchmark comparisons.
PHP only. Patterns and source files are parsed with nikic/php-parser, and any other --lang value is rejected as unsupported.
Not on cold scans. In the project's CI benchmark on the WordPress corpus, rg and grep were faster on every cold text query. Warm indexes help repeated queries, for example 10.90 ms against 49.61 ms for rg on an indexed count.
Greph runs single-process. Without pcntl_fork the worker pool runs every chunk in-process, with the same result as -j 1, and raises no error.
Not outside a match. Code outside the matched byte ranges keeps its whitespace, comments and layout. The matched code is re-printed from the template, so its line breaks are normalized and a comment inside it can be lost. Run with --dry-run first to print the rewritten files without writing them.
In .greph-index, .greph-ast-index and .greph-ast-cache at the indexed root by default. Pass --index-dir to any greph-index subcommand to use another location.
Read the Greph docs for the CLI reference, the PHP API, the indexed modes and the rg and sg wrappers.