composer/pcre

repository·main·Indexed 20 days ago

https://github.com/composer/pcre

A PHP library providing a robust wrapper and validation layer for PCRE (Perl Compatible Regular Expressions). It offers type-safe wrappers via the Preg class and result objects via the Regex class, ensuring predictable return values and enforcing PREG_UNMATCHED_AS_NULL behavior. The library includes a PHPStan extension for improved type information and regex validation, and surfaces PCRE errors through the PcreException class.

Tokens
5.8K
Snippets
27
Records
32
Agent score
71%

What's inside composer/pcre

  1. Understand PREG_UNMATCHED_AS_NULL behavior

    main

    The library enforces PREG_UNMATCHED_AS_NULL for all matching and replacement callback functions.

    Implications:

    • All matching groups will always be present in the $matches array.
    • If a group did not match, its value will be null rather than the group being missing from the array or being an empty string.
    • This makes it easy to distinguish between a group that matched an empty string and a group that did not match at all.

    Example Comparison: For the pattern /(a)(b)*(c)(d)*/ against the string 'ac':

    FeatureWithout FlagWith PREG_UNMATCHED_AS_NULL
    Array Size45
    Group 2 (unmatched)'' (cannot tell if matched empty or not matched)null (explicitly unmatched)
    Group 4 (unmatched)Missing (requires isset())null (always present, easy to check with $m[4] !== null)
  2. PHP version requirements for composer/pcre

    main

    The required PHP version depends on the major version of the library you are using:

    • 3.x versions: PHP 7.4.0 or higher
    • 2.x versions: PHP 7.2.0 or higher
    • 1.x versions: PHP 5.3.2 or higher
  3. Configure the PHPStan extension for composer/pcre

    main

    The library includes a PHPStan extension that provides improved type information for $matches and performs regex validation.

    If you are not using phpstan/extension-installer, you must manually include the extension in your phpstan.neon configuration:

    includes:
        - vendor/composer/pcre/extension.neon
  4. Use the Preg class for type-safe PCRE functions

    main

    The Composer\Pcre\Preg class provides static wrappers for standard preg_* functions. Unlike native PHP functions, these methods throw a Composer\Pcre\PcreException if a match or replacement fails, ensuring that return values (like strings or arrays) are predictable and non-nullable.

    Key methods include:

    • match(): Returns bool (via isMatch) or populates $matches.
    • matchAll(): For all matches.
    • replace(): Returns the resulting string.
    • replaceCallback(): For callback-based replacements.
    • grep(): Filters elements in an array.
    • split(): Splits a string into an array.

    Note: grep and split are only available via the Preg class.

    use Composer	extbackslash Pcre	extbackslash 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);
  5. Use matchStrictGroups for non-nullable match groups

    main

    The *StrictGroups variants (e.g., matchStrictGroups, matchAllStrictGroups) ensure that match groups are always present and non-nullable. If a subpattern does not match and would normally produce a null, these methods will throw an exception.

    When to use: This is safe as long as you do not have optional subpatterns (like (something)? or (something)*) that might not be matched at all. A subpattern that matches an empty string (like (.*)) is considered non-optional and is safe to use with these methods.

    use Composer	extbackslash Pcre	extbackslash 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)) {}
  6. Use the Regex class for result objects

    main

    If you prefer a more verbose API where by-reference arguments are replaced by result objects, use the Composer\Pcre\Regex class. This is particularly useful when you want to check if a match occurred using a boolean property on the returned object.

    Available result objects include MatchResult, MatchWithOffsetsResult, MatchAllResult, MatchAllWithOffsetsResult, and ReplaceResult.

    use Composer	extbackslash Pcre	extbackslash Regex;
    
    // Returns a bool instead of int(1/0)
    $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;
  7. Restrictions and limitations of composer/pcre

    main

    To maintain type safety, the following restrictions apply:

    • Offsets: You cannot pass PREG_OFFSET_CAPTURE to match() or matchAll(). Instead, use matchWithOffsets() and matchAllWithOffsets(). Similarly, use splitWithOffsets() instead of split() if you need offsets.
    • Order: matchAll() rejects PREG_SET_ORDER because it changes the shape of the returned matches.
    • Filtering: preg_filter is not supported. Use Preg::grep() combined with a loop and Preg::replace() instead.
    • Subjects: replace(), replaceCallback(), and replaceCallbackArray() only support string subjects; they do not support arrays.
    • Flags: The library always uses PREG_UNMATCHED_AS_NULL for matching and (as of v3.0) for replacement callbacks.
  8. Replace text using a callback with Regex::replaceCallback()

    main

    Use Regex::replaceCallback() to perform complex replacements using a callable. The callback receives the matches and returns the replacement string.

    Flags:

    • PREG_UNMATCHED_AS_NULL is always set.
    • PREG_OFFSET_CAPTURE is supported.
    $result = \Composer\Pcre\Regex::replaceCallback('/(\d+)/', function ($matches) {
        return (int)$matches[0] * 2;
    }, 'number 10');
  9. Filter arrays with Preg::grep()

    main

    Use Preg::grep() to filter elements of an array using a regular expression. This is a wrapper around preg_grep.

    Returns: An array containing only the elements that matched the pattern.

    $array = ['apple', 'banana', 'cherry'];
    $result = \Composer\Pcre\Preg::grep('/a/', $array);
    // ['apple', 'banana']
  10. Perform pattern matching with Preg::match()

    main

    Use Preg::match() to perform a single pattern match against a subject string. This method is a type-safe wrapper around preg_match that always includes the PREG_UNMATCHED_AS_NULL flag, ensuring that unmatched groups in the $matches array are represented as null rather than being omitted.

    Returns: 0 if no match was found, 1 if a match was found, or throws a PcreException if the underlying PCRE function fails.

    $pattern = '/foo/i';
    $subject = 'The quick brown fox';
    $matches = [];
    
    $result = \Composer\Pcre\Preg::match($pattern, $subject, $matches);
    // $result is 1 if matched
    // $matches contains the captured groups
  11. Perform strict pattern matching with Preg::matchStrictGroups()

    main

    Use Preg::matchStrictGroups() when you require that all captured groups in a pattern are non-null. This is a variant of match() that enforces strictness: if any group in the pattern is unmatched (resulting in a null value), it throws an UnexpectedNullMatchException.

    Returns: 0 if no match was found, 1 if a match was found, or throws UnexpectedNullMatchException if a group is null.

    // This will throw UnexpectedNullMatchException if the group (bar) is not found
    $pattern = '/foo(bar)?/';
    $subject = 'foo';
    $matches = [];
    
    \Composer\Pcre\Preg::matchStrictGroups($pattern, $subject, $matches);