A sane interface for php's built in preg_* functions

Overview

Making regex great again

Latest Version on Packagist Tests Software License Total Downloads

Php's built in preg_* functions require some odd patterns like passing variables by reference and treating false or null values as errors. spatie/regex provides a cleaner interface for preg_match, preg_match_all, preg_replace and preg_replace_callback.

use Spatie\Regex\Regex;

// Using `match`
Regex::match('/a/', 'abc'); // `MatchResult` object
Regex::match('/a/', 'abc')->hasMatch(); // true
Regex::match('/a/', 'abc')->result(); // 'a'

// Capturing groups with `match`
Regex::match('/a(b)/', 'abc')->result(); // 'ab'
Regex::match('/a(b)/', 'abc')->group(1); // 'b'

// Setting defaults
Regex::match('/a(b)/', 'xyz')->resultOr('default'); // 'default'
Regex::match('/a(b)/', 'xyz')->groupOr(1, 'default'); // 'default'

// Using `matchAll`
Regex::matchAll('/a/', 'abcabc')->hasMatch(); // true
Regex::matchAll('/a/', 'abcabc')->results(); // Array of `MatchResult` objects

// Using replace
Regex::replace('/a/', 'b', 'abc')->result(); // 'bbc';
Regex::replace('/a/', function (MatchResult $result) {
    return $result->result() . 'Hello!';
}, 'abc')->result(); // 'aHello!bc';

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/regex

Usage

Matching a pattern once

Matches a pattern on a subject. Returns a MatchResult object for the first match.

/**
 * @param string $pattern
 * @param string $subject
 *
 * @return \Spatie\Regex\MatchResult
 */
Regex::match(string $pattern, string $subject): MatchResult

MatchResult::hasMatch(): bool

Checks if the pattern matches the subject.

Regex::match('/abc/', 'abc')->hasMatch(); // true
Regex::match('/def/', 'abc')->hasMatch(); // false

MatchResult::result(): string

Return the full match that was made. Returns null if no match was made.

Regex::match('/abc/', 'abc')->result(); // 'abc'
Regex::match('/def/', 'abc')->result(); // null

MatchResult::group(int $id): string

Return the contents of a captured group (with a 1-based index). Throws a RegexFailed exception if the group doesn't exist.

Regex::match('/a(b)c/', 'abc')->group(1); // 'b'
Regex::match('/a(b)c/', 'abc')->group(2); // `RegexFailed` exception

Matching all occurences of a pattern

Matches a pattern on a subject. Returns a MatchAllResult object containing all matches.

/**
 * @param string $pattern
 * @param string $subject
 *
 * @return \Spatie\Regex\MatchAllResult
 */
public static function matchAll(string $pattern, string $subject): MatchAllResult

MatchAllResult::hasMatch(): bool

Checks if the pattern matches the subject.

Regex::matchAll('/abc/', 'abc')->hasMatch(); // true
Regex::matchAll('/abc/', 'abcabc')->hasMatch(); // true
Regex::matchAll('/def/', 'abc')->hasMatch(); // false

MatchAllResult::results(): array

Returns an array of MatchResult objects.

$results = Regex::matchAll('/ab([a-z])/', 'abcabd')->results();

$results[0]->result(); // 'abc'
$results[0]->group(1); // 'c'
$results[1]->result(); // 'abd'
$results[1]->group(1); // 'd'

Replacing a pattern in a subject

Replaces a pattern in a subject. Returns a ReplaceResult object.

/**
 * @param string|array $pattern
 * @param string|array|callable $replacement
 * @param string|array $subject
 * @param int $limit
 *
 * @return \Spatie\Regex\ReplaceResult
 */
public static function replace($pattern, $replacement, $subject, $limit = -1): ReplaceResult

ReplaceResult::result(): mixed

Regex::replace('/a/', 'b', 'abc')->result(); // 'bbc'

Regex::replace also works with callables. The callable will receive a MatchResult instance as it's argument.

Regex::replace('/a/', function (MatchResult $matchResult) {
    return str_repeat($matchResult->result(), 2);
}, 'abc')->result(); // 'aabc'

Patterns, replacements and subjects can also be arrays. Regex::replace behaves exactly like preg_replace in those instances.

Error handling

If anything goes wrong in a Regex method, a RegexFailed exception gets thrown. No need for checking preg_last_error().

Testing

$ composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Cannot replace with `_`

    Cannot replace with `_`

    Using

    Regex::replace('/[^a-zA-Z0-9_]+/', '_', $value)->result();
    

    fails with this error message:

    _() expects parameter 1 to be string, object given
    

    It seems like the _ string is detected as a callable as it may refer to gettext's _() function

    bug good first issue 
    opened by msiemens 8
  • Failing tests for PHP 7.3

    Failing tests for PHP 7.3

    Travis reports these failures for PHP 7.3. This should be fixed.

    1. Spatie\Regex\Test\MatchAllTest::it_throws_an_exception_if_a_match_throws_a_preg_error array_flip(): Can only flip STRING and INTEGER values! /home/travis/build/spatie/regex/src/RegexResult.php:9 /home/travis/build/spatie/regex/src/MatchAllResult.php:49 /home/travis/build/spatie/regex/src/Regex.php:26 /home/travis/build/spatie/regex/tests/MatchAllTest.php:54
    2. Spatie\Regex\Test\MatchTest::it_throws_an_exception_if_a_match_throws_a_preg_error array_flip(): Can only flip STRING and INTEGER values!
    bug help wanted good first issue 
    opened by freekmurze 3
  • Allow default value when group doesn't exist

    Allow default value when group doesn't exist

    Hey there, I have a sittuation where a regex group might not exist and where I would like to return a default value instead. With the current implementation I would need to do a check first and handle that accordingly. What if we could specify a default value as a parameter? I was about to submit a PR but I would like some feedback first, let me know if you think this would be useful:

    How I do it now:

    $pattern = '/some-pattern/(.+?)/with-groups';
    
    if(Regex::match($pattern, $url)->hasMatch()){
        $value = Regex::match($pattern, $url)->group(1);
    }) else {
        $value = 'something else';   
    };
    
    // or
    $regex = Regex::match($pattern, $url);
    $value = $regex->hasMatch() ? $regex->group(1) : 'something else';
    

    Proposed way:

    // this approach would add a default parameter to keep backwards compatibility
    $value = Regex::match('/some-pattern/(.+?)/with-groups', $url)->group(1, 'something else');
    
    // or a new method
    $value = Regex::match('/some-pattern/(.+?)/with-groups', $url)->groupOr(1, 'something else');
    
    // or maybe simply return null or false?
    $value = Regex::match('/some-pattern/(.+?)/with-groups', $url)->group(1) ?? 'something else';
    

    Anyway, it might not even be worth it...

    enhancement 
    opened by luiz-brandao 3
  • [Discussion] Integrate SRL as option

    [Discussion] Integrate SRL as option

    Does it make sense to integrate https://github.com/TYVRNET/SRL in this package to have one simple Regex API? Regardless if one want to use raw regex or SRL.

    opened by okaufmann 3
  • Add PHP 8-only support (v2)

    Add PHP 8-only support (v2)

    This PR adds a new major version, v2.0.0.

    Specifically, it:

    • Removes support for all PHP 7.x versions.
    • Requires PHP 8.0+.
    • All syntax converted to PHP 8 where possible.
    • Removes unnecessary PHP docblocks per Spatie's guidelines.
    • Moves exception classes to an "Exceptions" directory to match the structure of other packages.
    • Adds config & github workflow for php-cs-fixer.
    • Updates the location of CONTRIBUTING.md.
    • Updates the readme with minor adjustments from spatie/package-skeleton-php.
    • Updates the changelog - release date needs to be updated, currently is "unreleased".
    opened by patinthehat 2
  • Expensive

    Expensive

    Just looking at the documentation:

    // Using `match`
    Regex::match('/a/', 'abc'); // `MatchResult` object
    Regex::match('/a/', 'abc')->hasMatch(); // true
    Regex::match('/a/', 'abc')->result(); // 'a'
    

    I realize this is an example, but it's not uncommon for regex to be expensive to process, and this static approach would reevaluate the regex for each statement.

    An object-oriented approach would be better, or perhaps some caching of results to prevent needlessly processing the same patterns and string combinations more than once.

    Alternatively, remove hasMatch() and suggest checking the result() and results() to determine if there are matches.

    opened by Sarke 2
  • Request: Retrieve array of groups.

    Request: Retrieve array of groups.

    First of all, I ❤️ this library as well as all of your projects! 🤓

    I think it would be really convenient to be able to access the private MatchResult::$matches property to be able to access the results as an array.

    Example

    $result = Regex::match($pattern, $subject);
    
    // Either as a property...
    $result->matches;
    
    // or as an accessor method...
    $result->groups()
    
    // Use
    $results = $result->groups();
    echo($results[3]); // Printing group #3.
    

    Could also probably apply to MatchAllResult::class as well.

    Thank you for all of your work!

    help wanted good first issue 
    opened by nmsmith22389 2
  • Support for named capturing groups in MatchAllResult

    Support for named capturing groups in MatchAllResult

    I added support for named capturing groups in class MatchAllResult. The old behavior has been done with array_map() but it's not compatible with string keys inside arrays, so they can't be unpacked. I therefore reworked it and now named capturing groups in regular expressions can be used on Regex::matchAll() also.

    As an additional change I reworked MatchResult::group() and MatchResult::namedGroup(). I don't see why I need to distinguish between indexed and named capturing groups, because probably if using named capturing groups they can be the same. I think there's no need to differentiate between both.

    Additionally I type hinted MatchAllResult::results() to get a useful code completion.

    I also annotated necessary @throws tags where an exception could be thrown.

    opened by martinhartmann 2
  •  Make it possible to create regular expressions programmatically

    Make it possible to create regular expressions programmatically

    This PR contains a RegexBuilder and an ExpressionBuilder that makes it easy to generate regular expressions programmatically. It makes it easy to understand previously written regexes, to use advanced expressions and to tell what the regex is doing to beginning regex programmers.

    Example:

    $builder = RegexBuilder::create();
    $expr = ExpressionBuilder::create();
    $regex = $builder
        ->setDelimiter('/')
        ->isCaseInsensitive()
        ->isFreeSpacing()
        ->addExpression($expr->extendedComment('This one matches zero or more numeric characters.'))
        ->addExpression(
            $exp->concat(
                $exp->characterClass(
                    $exp->concat(
                        $exp->range('0', '9'),
                        $exp->range('a', 'z')
                    )
                ),
                $exp->zeroOrMoreTimes()
            )
        )
        ->getRegex("\n");
    

    Result

    /
    # This one matches zero or more numeric characters.
    [0-9a-z]*
    /ix
    

    (Off course the example above is simple and can be written directly as the result, but some advanced expressions are also available with the builder.)

    It's based on this presentation: http://slides.seld.be/?file=2016-01-30+How+I+learned+to+stop+worrying+and+love+Regular+Expressions.html#1

    If this is something you want inside this package, I can add some additional documentation on how to use it.

    (Side-Note: I thing the current Regex class should really be a Regex instance instead of a list with static functions. It makes more sense to me)

    opened by veewee 2
  • A non-static RegExp object

    A non-static RegExp object

    Its not a feature request but more like a philosophical talk.

    Did you consider a non-static approach, something like Js Regexp.

    Then you could do for example:

    $re = new RegExp(...);
    
    Str::endsWith('abcde', $re);
    
    //--
    
    class Str
    {
        public static function endsWith(string $haystack, $needle): bool
        {
            $length = strlen($needle);
            if ($length == 0) {
                return true;
            }
            
            if ($needle instanceof RegExp) {
                return $needle->match($haystack)->hasMatch();
            } else {
                return substr($haystack, -$length) === $needle;
            }
        }
    }
    

    Otherwise very nice library.

    opened by kosmodisk 1
  • Add replaceString and replaceArray methods

    Add replaceString and replaceArray methods

    Regex::replace can be used with both strings and arrays for $subject (like the original preg_replace method), and it therefore can return a string or an array, which leads to static analyzers (like PHPStan) not being able to tell what the return type ends up being - making the use of static analyzers very painful or simply impossible, as it will be necessary to type cast or somehow tell the static analyzers the exact type each time.

    I suggest adding methods for both cases, one where $subject specifically is a string, and one where $subject is an array, so the return type can be specified. These functions could simply be named replaceString and replaceArray - that way it could be added without any backwards compatibility problems.

    I can prepare a PR if this is something that you are open to.

    opened by iquito 1
Releases(3.1.1)
Owner
Spatie
Webdesign agency based in Antwerp, Belgium
Spatie
ATOMASTIC 14 Mar 12, 2022
🉑 Portable UTF-8 library - performance optimized (unicode) string functions for php.

?? Portable UTF-8 Description It is written in PHP (PHP 7+) and can work without "mbstring", "iconv" or any other extra encoding php-extension on your

Lars Moelleken 474 Dec 22, 2022
🔡 Portable ASCII library - performance optimized (ascii) string functions for php.

?? Portable ASCII Description It is written in PHP (PHP 7+) and can work without "mbstring", "iconv" or any other extra encoding php-extension on your

Lars Moelleken 380 Jan 6, 2023
A PHP class which allows the decoding and encoding of a wider variety of characters compared to the standard htmlentities and html_entity_decode functions.

The ability to encode and decode a certain set of characters called 'Html Entities' has existed since PHP4. Amongst the vast number of functions built into PHP, there are 4 nearly identical functions that are used to encode and decode html entities; despite their similarities, however, 2 of them do provide additional capabilities not available to the others.

Gavin G Gordon (Markowski) 2 Nov 12, 2022
"結巴"中文分詞:做最好的 PHP 中文分詞、中文斷詞組件。 / "Jieba" (Chinese for "to stutter") Chinese text segmentation: built to be the best PHP Chinese word segmentation module.

jieba-php "結巴"中文分詞:做最好的 PHP 中文分詞、中文斷詞組件,目前翻譯版本為 jieba-0.33 版本,未來再慢慢往上升級,效能也需要再改善,請有興趣的開發者一起加入開發!若想使用 Python 版本請前往 fxsjy/jieba 現在已經可以支援繁體中文!只要將字典切換為 bi

Fukuball Lin 1.2k Dec 31, 2022
A simple, standalone, modern PHP class inspector and mapper library, wrapping PHPs native reflection in a fluent interface

A simple, standalone, modern PHP class inspector and mapper library, wrapping PHPs native reflection in a fluent interface.

smpl 9 Sep 1, 2022
YCOM Impersonate. Login as selected YCOM user 🧙‍♂️in frontend.

YCOM Impersonate Login as selected YCOM user in frontend. Features: Backend users with admin rights or YCOM[] rights, can be automatically logged in v

Friends Of REDAXO 17 Sep 12, 2022
A fluent extension to PHPs DateTime class.

Expressive Date A fluent extension to PHPs DateTime class. Table of Contents Installation Composer Manually Laravel 4 Usage Getting Instances Quick He

Jason Lewis 258 Oct 9, 2021
PCRE wrapping library that offers type-safe preg_* replacements.

composer/pcre PCRE wrapping library that offers type-safe preg_* replacements. If you are using a modern PHP version you are probably better off using

Composer 308 Dec 30, 2022
A Chainable, REST Friendly, PHP HTTP Client. A sane alternative to cURL.

Httpful Httpful is a simple Http Client library for PHP 7.2+. There is an emphasis of readability, simplicity, and flexibility – basically provide the

Nate Good 1.7k Dec 21, 2022
An extensible validation library for your data with sane defaults.

Hird Hirds, also known as housecarls, was a gathering of hirdmen, who functioned as the king's personal guards during the viking age and the early mid

Asko Nõmm 13 Apr 23, 2022
BetterWPDB - Keeps you safe and sane when working with custom tables in WordPress.

BetterWPDB - Keeps you safe and sane when working with custom tables in WordPress.

Snicco 21 Dec 15, 2022
Finally a sane way to register available commands and arguments and match your command line in PHP

clue/commander Finally a sane way to register available commands and arguments and match your command line in PHP. You want to build a command line in

Christian Lück 172 Nov 27, 2022
Magento-Functions - A Resource of Magento Functions

Magento-Functions A Resource of Magento Functions Table of Contents Category Product User Cart Checkout General Account [Working w/ URL's] (#urls) Cat

Bryan Littlefield 28 Apr 19, 2021
Simple MySQL library for PHP 5.4+ includes Query Builder, PDO Native functions, Helper functions for quick use.

Simple MySQL library for PHP 5.4+ includes Query Builder, PDO Native functions, Helper functions for quick use.

Kodols 9 Dec 22, 2022
Here is the top 100 PHP functions: it is the list of the most often used PHP native functions

Here is the top 100 PHP functions: it is the list of the most often used PHP native functions. If you are a PHP developer, you must know the Top 100 PHP Functions deeply.

Max Base 16 Dec 11, 2022
ATOMASTIC 14 Mar 12, 2022
Mock built-in PHP functions (e.g. time(), exec() or rand())

PHP-Mock: mocking built-in PHP functions PHP-Mock is a testing library which mocks non deterministic built-in PHP functions like time() or rand(). Thi

null 331 Jan 3, 2023
Free and open-source Laravel admin dashboard interface built with Livewire & Alpine.js based on Bootstrap 5

Volt Dashboard Laravel Free Frontend Web App for Laravel with Livewire & Alpine.js Never start a development project from scratch again. We've partner

Themesberg 200 Jan 4, 2023
Auth is a module for the Yii PHP framework that provides a web user interface for Yii's built-in authorization manager

Auth is a module for the Yii PHP framework that provides a web user interface for Yii's built-in authorization manager (CAuthManager). You can read more about Yii's authorization manager in the framework documentation under Authentication and Authorization.

Christoffer Niska 134 Oct 22, 2022