PHPackages                             composer/pcre - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. composer/pcre

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

composer/pcre
=============

PCRE wrapping library that offers type-safe preg\_\* replacements.

3.3.2(1y ago)699313.8M—2.2%1520MITPHPPHP ^7.4 || ^8.0CI passing

Since Dec 6Pushed 1mo ago4 watchersCompare

[ Source](https://github.com/composer/pcre)[ Packagist](https://packagist.org/packages/composer/pcre)[ Fund](https://packagist.com)[ GitHub Sponsors](https://github.com/composer)[ RSS](/packages/composer-pcre/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (28)Used By (20)

composer/pcre
=============

[](#composerpcre)

PCRE wrapping library that offers type-safe `preg_*` replacements.

This library gives you a way to ensure `preg_*` functions do not fail silently, returning unexpected `null`s that may not be handled.

As of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage for all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null)to understand the implications.

It thus makes it easier to work with static analysis tools like PHPStan or Psalm as it simplifies and reduces the possible return values from all the `preg_*` functions which are quite packed with edge cases. As of v2.2.0 / v3.2.0 the library also comes with a [PHPStan extension](#phpstan-extension) for parsing regular expressions and giving you even better output types.

This library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations). If you are looking for a richer API to handle regular expressions have a look at [rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead.

[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions)

Installation
------------

[](#installation)

Install the latest version with:

```
$ composer require composer/pcre
```

Requirements
------------

[](#requirements)

- PHP 7.4.0 is required for 3.x versions
- PHP 7.2.0 is required for 2.x versions
- PHP 5.3.2 is required for 1.x versions

Basic usage
-----------

[](#basic-usage)

Instead of:

```
if (preg_match('{fo+}', $string, $matches)) { ... }
if (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... }
if (preg_match_all('{fo+}', $string, $matches)) { ... }
$newString = preg_replace('{fo+}', 'bar', $string);
$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);
$newString = preg_replace_callback_array(['{fo+}' => fn ($match) => strtoupper($match[0])], $string);
$filtered = preg_grep('{[a-z]}', $elements);
$array = preg_split('{[a-z]+}', $string);
```

You can now call these on the `Preg` class:

```
use Composer\Pcre\Preg;

if (Preg::match('{fo+}', $string, $matches)) { ... }
if (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... }
if (Preg::matchAll('{fo+}', $string, $matches)) { ... }
$newString = Preg::replace('{fo+}', 'bar', $string);
$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);
$newString = Preg::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string);
$filtered = Preg::grep('{[a-z]}', $elements);
$array = Preg::split('{[a-z]+}', $string);
```

The main difference is if anything fails to match/replace/.., it will throw a `Composer\Pcre\PcreException`instead of returning `null` (or false in some cases), so you can now use the return values safely relying on the fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split).

Additionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety when the number of pattern matches is not useful:

```
use Composer\Pcre\Preg;

if (Preg::isMatch('{fo+}', $string, $matches)) // bool
if (Preg::isMatchAll('{fo+}', $string, $matches)) // bool
```

Finally the `Preg` class provides a few `*StrictGroups` method variants that ensure match groups are always present and thus non-nullable, making it easier to write type-safe code:

```
use Composer\Pcre\Preg;

// $matches is guaranteed to be an array of strings, if a subpattern does not match and produces a null it will throw
if (Preg::matchStrictGroups('{fo+}', $string, $matches))
if (Preg::matchAllStrictGroups('{fo+}', $string, $matches))
```

**Note:** This is generally safe to use as long as you do not have optional subpatterns (i.e. `(something)?`or `(something)*` or branches with a `|` that result in some groups not being matched at all). A subpattern that can match an empty string like `(.*)` is **not** optional, it will be present as an empty string in the matches. A non-matching subpattern, even if optional like `(?:foo)?` will anyway not be present in matches so it is also not a problem to use these with `*StrictGroups` methods.

If you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class:

```
use Composer\Pcre\Regex;

// this is useful when you are just interested in knowing if something matched
// as it returns a bool instead of int(1/0) for match
$bool = Regex::isMatch('{fo+}', $string);

$result = Regex::match('{fo+}', $string);
if ($result->matched) { something($result->matches); }

$result = Regex::matchWithOffsets('{fo+}', $string);
if ($result->matched) { something($result->matches); }

$result = Regex::matchAll('{fo+}', $string);
if ($result->matched && $result->count > 3) { something($result->matches); }

$newString = Regex::replace('{fo+}', 'bar', $string)->result;
$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)->result;
$newString = Regex::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string)->result;
```

Note that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have complex return types warranting a specific result object.

See the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php), [MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details.

Restrictions / Limitations
--------------------------

[](#restrictions--limitations)

Due to type safety requirements a few restrictions are in place.

- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`. You cannot pass the flag to `match`/`matchAll`.
- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets`instead.
- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There is no alternative provided as you can fairly easily code around it.
- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather use `Preg::grep` in combination with some loop and `Preg::replace`.
- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`, only simple strings.
- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for `replaceCallback` and `replaceCallbackArray`.

#### PREG\_UNMATCHED\_AS\_NULL

[](#preg_unmatched_as_null)

As of 2.0, this library always uses PREG\_UNMATCHED\_AS\_NULL for all `match*` and `isMatch*`functions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`.

This means your matches will always contain all matching groups, either as null if unmatched or as string if it matched.

The advantages in clarity and predictability are clearer if you compare the two outputs of running this with and without PREG\_UNMATCHED\_AS\_NULL in $flags:

```
preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags);
```

no flagPREG\_UNMATCHED\_AS\_NULLarray (size=4)array (size=5)0 =&gt; string 'ac' (length=2)0 =&gt; string 'ac' (length=2)1 =&gt; string 'a' (length=1)1 =&gt; string 'a' (length=1)2 =&gt; string '' (length=0)2 =&gt; null3 =&gt; string 'c' (length=1)3 =&gt; string 'c' (length=1)4 =&gt; nullgroup 2 (any unmatched group preceding one that matched) is set to `''`. You cannot tell if it matched an empty string or did not match at allgroup 2 is `null` when unmatched and a string if it matched, easy to check forgroup 4 (any optional group without a matching one following) is missing altogether. So you have to check with `isset()`, but really you want `isset($m[4]) && $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows itgroup 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null`PHPStan Extension
-----------------

[](#phpstan-extension)

To use the PHPStan extension if you do not use `phpstan/extension-installer` you can include `vendor/composer/pcre/extension.neon` in your PHPStan config.

The extension provides much better type information for $matches as well as regex validation where possible.

License
-------

[](#license)

composer/pcre is licensed under the MIT License, see the LICENSE file for details.

###  Health Score

64

—

FairBetter than 99% of packages

Maintenance68

Regular maintenance activity

Popularity76

Solid adoption and visibility

Community34

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 81.9% 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 ~48 days

Recently: every ~68 days

Total

27

Last Release

356d ago

Major Versions

2.1.3 → 3.1.32024-03-19

2.2.0 → 3.2.02024-07-25

2.3.0 → 3.3.02024-08-19

2.3.1 → 3.3.12024-08-27

2.3.2 → 3.3.22024-11-12

PHP version history (3 changes)1.0.0PHP ^5.3.2 || ^7.0 || ^8.0

2.0.0PHP ^7.2 || ^8.0

3.0.0PHP ^7.4 || ^8.0

### Community

Maintainers

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

---

Top Contributors

[![Seldaek](https://avatars.githubusercontent.com/u/183678?v=4)](https://github.com/Seldaek "Seldaek (95 commits)")[![staabm](https://avatars.githubusercontent.com/u/120441?v=4)](https://github.com/staabm "staabm (8 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (5 commits)")[![johnstevenson](https://avatars.githubusercontent.com/u/881777?v=4)](https://github.com/johnstevenson "johnstevenson (2 commits)")[![prudloff-insite](https://avatars.githubusercontent.com/u/50333926?v=4)](https://github.com/prudloff-insite "prudloff-insite (1 commits)")[![rarila](https://avatars.githubusercontent.com/u/5936174?v=4)](https://github.com/rarila "rarila (1 commits)")[![pionl](https://avatars.githubusercontent.com/u/1878831?v=4)](https://github.com/pionl "pionl (1 commits)")[![shakaran](https://avatars.githubusercontent.com/u/14254?v=4)](https://github.com/shakaran "shakaran (1 commits)")[![GromNaN](https://avatars.githubusercontent.com/u/400034?v=4)](https://github.com/GromNaN "GromNaN (1 commits)")[![SchmidtClaudia](https://avatars.githubusercontent.com/u/1684565?v=4)](https://github.com/SchmidtClaudia "SchmidtClaudia (1 commits)")

---

Tags

regexPCREregular expressionpreg

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[spatie/regex

A sane interface for php's built in preg\_\* functions

1.1k17.1M59](/packages/spatie-regex)[regex-guard/regex-guard

A wrapper that allows you to validate regular expressions and handle normally uncatchable PCRE compilation warnings

24509.2k8](/packages/regex-guard-regex-guard)[jasny/twig-extensions

A set of useful Twig filters

10710.2M8](/packages/jasny-twig-extensions)[niklongstone/regex-reverse

Regular Expression reverter, generates a string from a provided regular expression

105150.4k4](/packages/niklongstone-regex-reverse)[gherkins/regexpbuilderphp

PHP port of thebinarysearchtree/regexpbuilderjs

1.4k163.0k1](/packages/gherkins-regexpbuilderphp)[rawr/t-regx

PHP regular expression brought up to modern standards.

451157.3k3](/packages/rawr-t-regx)

PHPackages © 2026

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