{"id":14985350,"url":"https://github.com/composer/pcre","last_synced_at":"2025-05-13T20:18:57.349Z","repository":{"id":37518861,"uuid":"433598933","full_name":"composer/pcre","owner":"composer","description":"PCRE wrapping library that offers type-safe preg_* replacements.","archived":false,"fork":false,"pushed_at":"2024-11-20T09:10:58.000Z","size":171,"stargazers_count":616,"open_issues_count":0,"forks_count":14,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-28T11:54:51.318Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/composer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://packagist.com","tidelift":"packagist/composer/composer","github":"composer"}},"created_at":"2021-11-30T21:58:41.000Z","updated_at":"2025-04-28T09:01:14.000Z","dependencies_parsed_at":"2024-12-10T12:02:36.736Z","dependency_job_id":"09cdf190-1fc6-4e02-9831-6bb4d2df5c9a","html_url":"https://github.com/composer/pcre","commit_stats":{"total_commits":92,"total_committers":9,"mean_commits":"10.222222222222221","dds":"0.17391304347826086","last_synced_commit":"b2bed4734f0cc156ee1fe9c0da2550420d99a21e"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fpcre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fpcre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fpcre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composer%2Fpcre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/composer","download_url":"https://codeload.github.com/composer/pcre/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254020659,"owners_count":22000757,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T14:10:45.241Z","updated_at":"2025-05-13T20:18:57.297Z","avatar_url":"https://github.com/composer.png","language":"PHP","funding_links":["https://packagist.com","https://tidelift.com/funding/github/packagist/composer/composer","https://github.com/sponsors/composer"],"categories":[],"sub_categories":[],"readme":"composer/pcre\n=============\n\nPCRE wrapping library that offers type-safe `preg_*` replacements.\n\nThis library gives you a way to ensure `preg_*` functions do not fail silently, returning\nunexpected `null`s that may not be handled.\n\nAs of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage\nfor all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null)\nto understand the implications.\n\nIt thus makes it easier to work with static analysis tools like PHPStan or Psalm as it\nsimplifies and reduces the possible return values from all the `preg_*` functions which\nare quite packed with edge cases. As of v2.2.0 / v3.2.0 the library also comes with a\n[PHPStan extension](#phpstan-extension) for parsing regular expressions and giving you even better output types.\n\nThis library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations).\nIf you are looking for a richer API to handle regular expressions have a look at\n[rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead.\n\n[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions)\n\n\nInstallation\n------------\n\nInstall the latest version with:\n\n```bash\n$ composer require composer/pcre\n```\n\n\nRequirements\n------------\n\n* PHP 7.4.0 is required for 3.x versions\n* PHP 7.2.0 is required for 2.x versions\n* PHP 5.3.2 is required for 1.x versions\n\n\nBasic usage\n-----------\n\nInstead of:\n\n```php\nif (preg_match('{fo+}', $string, $matches)) { ... }\nif (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... }\nif (preg_match_all('{fo+}', $string, $matches)) { ... }\n$newString = preg_replace('{fo+}', 'bar', $string);\n$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);\n$newString = preg_replace_callback_array(['{fo+}' =\u003e fn ($match) =\u003e strtoupper($match[0])], $string);\n$filtered = preg_grep('{[a-z]}', $elements);\n$array = preg_split('{[a-z]+}', $string);\n```\n\nYou can now call these on the `Preg` class:\n\n```php\nuse Composer\\Pcre\\Preg;\n\nif (Preg::match('{fo+}', $string, $matches)) { ... }\nif (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... }\nif (Preg::matchAll('{fo+}', $string, $matches)) { ... }\n$newString = Preg::replace('{fo+}', 'bar', $string);\n$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);\n$newString = Preg::replaceCallbackArray(['{fo+}' =\u003e fn ($match) =\u003e strtoupper($match[0])], $string);\n$filtered = Preg::grep('{[a-z]}', $elements);\n$array = Preg::split('{[a-z]+}', $string);\n```\n\nThe main difference is if anything fails to match/replace/.., it will throw a `Composer\\Pcre\\PcreException`\ninstead of returning `null` (or false in some cases), so you can now use the return values safely relying on\nthe fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split).\n\nAdditionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety\nwhen the number of pattern matches is not useful:\n\n```php\nuse Composer\\Pcre\\Preg;\n\nif (Preg::isMatch('{fo+}', $string, $matches)) // bool\nif (Preg::isMatchAll('{fo+}', $string, $matches)) // bool\n```\n\nFinally the `Preg` class provides a few `*StrictGroups` method variants that ensure match groups\nare always present and thus non-nullable, making it easier to write type-safe code:\n\n```php\nuse Composer\\Pcre\\Preg;\n\n// $matches is guaranteed to be an array of strings, if a subpattern does not match and produces a null it will throw\nif (Preg::matchStrictGroups('{fo+}', $string, $matches))\nif (Preg::matchAllStrictGroups('{fo+}', $string, $matches))\n```\n\n**Note:** This is generally safe to use as long as you do not have optional subpatterns (i.e. `(something)?`\nor `(something)*` or branches with a `|` that result in some groups not being matched at all).\nA subpattern that can match an empty string like `(.*)` is **not** optional, it will be present as an\nempty string in the matches. A non-matching subpattern, even if optional like `(?:foo)?` will anyway not be present in\nmatches so it is also not a problem to use these with `*StrictGroups` methods.\n\nIf you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class:\n\n```php\nuse Composer\\Pcre\\Regex;\n\n// this is useful when you are just interested in knowing if something matched\n// as it returns a bool instead of int(1/0) for match\n$bool = Regex::isMatch('{fo+}', $string);\n\n$result = Regex::match('{fo+}', $string);\nif ($result-\u003ematched) { something($result-\u003ematches); }\n\n$result = Regex::matchWithOffsets('{fo+}', $string);\nif ($result-\u003ematched) { something($result-\u003ematches); }\n\n$result = Regex::matchAll('{fo+}', $string);\nif ($result-\u003ematched \u0026\u0026 $result-\u003ecount \u003e 3) { something($result-\u003ematches); }\n\n$newString = Regex::replace('{fo+}', 'bar', $string)-\u003eresult;\n$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)-\u003eresult;\n$newString = Regex::replaceCallbackArray(['{fo+}' =\u003e fn ($match) =\u003e strtoupper($match[0])], $string)-\u003eresult;\n```\n\nNote that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have\ncomplex return types warranting a specific result object.\n\nSee the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php),\n[MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details.\n\nRestrictions / Limitations\n--------------------------\n\nDue to type safety requirements a few restrictions are in place.\n\n- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`.\n  You cannot pass the flag to `match`/`matchAll`.\n- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets`\n  instead.\n- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There\n  is no alternative provided as you can fairly easily code around it.\n- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather\n  use `Preg::grep` in combination with some loop and `Preg::replace`.\n- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`,\n  only simple strings.\n- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much\n  saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for\n  `replaceCallback` and `replaceCallbackArray`.\n\n#### PREG_UNMATCHED_AS_NULL\n\nAs of 2.0, this library always uses PREG_UNMATCHED_AS_NULL for all `match*` and `isMatch*`\nfunctions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`.\n\nThis means your matches will always contain all matching groups, either as null if unmatched\nor as string if it matched.\n\nThe advantages in clarity and predictability are clearer if you compare the two outputs of\nrunning this with and without PREG_UNMATCHED_AS_NULL in $flags:\n\n```php\npreg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags);\n```\n\n| no flag | PREG_UNMATCHED_AS_NULL |\n| --- | --- |\n| array (size=4)              | array (size=5) |\n| 0 =\u003e string 'ac' (length=2) |   0 =\u003e string 'ac' (length=2) |\n| 1 =\u003e string 'a' (length=1)  |   1 =\u003e string 'a' (length=1) |\n| 2 =\u003e string '' (length=0)   |   2 =\u003e null |\n| 3 =\u003e string 'c' (length=1)  |   3 =\u003e string 'c' (length=1) |\n|                             |   4 =\u003e null |\n| group 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 all | group 2 is `null` when unmatched and a string if it matched, easy to check for |\n| group 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]) \u0026\u0026 $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows it | group 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null` |\n\nPHPStan Extension\n-----------------\n\nTo use the PHPStan extension if you do not use `phpstan/extension-installer` you can include `vendor/composer/pcre/extension.neon` in your PHPStan config.\n\nThe extension provides much better type information for $matches as well as regex validation where possible.\n\nLicense\n-------\n\ncomposer/pcre is licensed under the MIT License, see the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposer%2Fpcre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomposer%2Fpcre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposer%2Fpcre/lists"}