Search by

php-regex / regex-linter

yoeunes

Lints the regex patterns of a PHP code base: extraction from PHP sources, lint rules, ReDoS and validity checks, reports in console, JSON, GitHub, Checkstyle and JUnit formats.

Package info

github.com/php-regex/regex-linter

Homepage

pkg:composer/php-regex/regex-linter

Fund package maintenance!

yoeunes

Statistics

Installs: 0

Dependents: 6

Suggesters: 0

Stars: 0

2.x-dev 2026-10-05 09:20 UTC

This package is auto-updated.

Last update: 2026-10-05 09:21:14 UTC


README

PHPRegex Linter

PHPRegex Linter

Lints the regex patterns of a PHP codebase: extraction from PHP sources, lint rules, ReDoS and validity checks, reports in console, JSON, GitHub, Checkstyle and JUnit formats.

Features

  • 32 lint rules over the parsed AST: redundant groups and character classes, useless flags and quantifiers, lazy quantifiers that end the pattern, suspicious escapes and ranges, undefined backreferences, impossible anchors, multibyte text PCRE reads as bytes without /u.
  • Patterns are validated before they are linted: parse and semantic errors arrive with a position and a tip, not a guess.
  • Extraction from PHP sources: native preg_* calls, the composer/pcre, nette/utils, spatie/regex and Laravel Str wrappers, and your own helper functions — with @regex-ignore comments to suppress a finding inline.
  • ReDoS detection through php-regex/regex-redos: proven verdicts with their attack input, theoretical or confirmed mode, four severity thresholds.
  • Optimization suggestions through php-regex/regex-optimizer, each rewrite checked for equivalence by the automata solver.
  • Five report formats: console, JSON, GitHub annotations, Checkstyle, JUnit.

Installation

composer require php-regex/regex-linter

Requires PHP 8.2+. The extractor runs on the PHP tokenizer by default; install nikic/php-parser to extract from a full PHP parser instead.

To lint a whole codebase from the terminal, add the console package:

composer require --dev php-regex/regex-cli
vendor/bin/regex lint src/

Configuration

The lint command reads a regex.json (committed) or regex.dist.json (template) from the project root. This package ships the JSON Schema as regex.schema.json, so editors validate the file as you type.

Key Default What it does
paths, exclude working directory, vendor Directories or files to scan, and paths to skip
phpVersion, pcreVersion from composer.json The PHP ("8.3", 80300, "runtime") and PCRE2 ("10.44") releases the patterns are judged for
jobs CPU cores Parallel workers
format console console, json, github, checkstyle or junit
ide none phpstorm, vscode, others, or a URL template — for clickable links
extraction.interop ["composer-pcre"] Wrapper presets to read: composer-pcre, nette-utils, spatie-regex, laravel-str
extraction.functions [] Project helpers carrying a pattern, as App\Support\Str::matches#1
checks.validation true Syntax and semantic validation
checks.redos.enabled false ReDoS analysis
checks.redos.mode, checks.redos.threshold theoretical, high Analysis mode, and the lowest severity reported
checks.optimizations.enabled, checks.optimizations.minSavings true, 1 Optimization suggestions, and the minimum characters saved to report one
checks.lint.enabled true Lint rules
checks.lint.rules.<id> true One boolean per rule id; unicode.shorthandWithoutU defaults to false

Usage

PatternLinter is a node visitor: run it on any parsed AST.

use PHPRegex\Linter\PatternLinter;
use PHPRegex\Parser\RegexParser;

$linter = new PatternLinter();
RegexParser::create()->parse('/(a+)+b/')->accept($linter);

foreach ($linter->getIssues() as $violation) {
    echo $violation->id, ': ', $violation->message, "\n";
}
regex.lint.quantifier.nested: Nested quantifiers can cause catastrophic backtracking.
regex.lint.group.quantifiedCapture: Quantified capturing group "(...)" with "+": only the last iteration's capture is retained.

Turn rules on or off by id — the same map checks.lint.rules reads from regex.json:

use PHPRegex\Linter\PatternLinter;
use PHPRegex\Parser\RegexParser;

$linter = new PatternLinter([
    'quantifier.nested' => false,        // disable one rule
    'unicode.shorthandWithoutU' => true, // enable the opt-in rule
]);
RegexParser::create()->parse('/^\w+\d*$/')->accept($linter);

foreach ($linter->getIssues() as $violation) {
    echo $violation->id, ': ', $violation->message, "\n";
}
regex.lint.quantifier.concatenation: Concatenated quantifiers can be optimized when one character set is a subset of the other.
regex.lint.unicode.shorthandWithoutU: Shorthand "\w" matches only ASCII without /u flag.
regex.lint.unicode.shorthandWithoutU: Shorthand "\d" matches only ASCII without /u flag.

A whole codebase, from the terminal — exits non-zero when a pattern does not compile, a confirmed ReDoS verdict reaches high, or a rule of error severity fires (the /u rules: a multibyte character in a class, a quantified multibyte character, a Unicode property); --format=github fits CI:

vendor/bin/regex lint src/
  demo.php:4:30
      → /(a+)+b/
    WARN Nested quantifiers can cause catastrophic backtracking.
         ↳ Consider atomic groups (?>...) or possessive quantifiers — verify the rewrite still matches everything you need.

Documentation

  • Quick start — the lint command among the first five to know
  • CLI guide — every regex lint option and the full regex.json reference
  • Diagnostics — how issues are reported and how to read them
  • ReDoS guide — risky shapes, detection modes and mitigations

This package is part of PHPRegex, released with its siblings under one version number; read the backward compatibility promise.

Resources

Sponsors

Sponsor

If PHPRegex saves you time, consider sponsoring its maintenance.

License

MIT. See LICENSE.