Giter Site home page Giter Site logo

spatie / regex Goto Github PK

View Code? Open in Web Editor NEW
1.1K 13.0 45.0 91 KB

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

Home Page: https://freek.dev/549-a-package-to-easily-work-with-regex-in-php

License: MIT License

PHP 100.00%
php regular-expression regex hacktoberfest

regex's Introduction

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.

regex's People

Contributors

adrianmrn avatar akoepcke avatar alexvanderbist avatar carusogabriel avatar cv21 avatar einsteinpp avatar freekmurze avatar gpressutto5 avatar grahamcampbell avatar introwit avatar karelwintersky avatar lasserafn avatar luiz-brandao avatar nathanael-shermett avatar patinthehat avatar peter279k avatar pippinsmith avatar riasvdv avatar sebastiandedeyne avatar seldaek avatar snapshotpl avatar staabm avatar tjoosten avatar xeno010 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

regex's Issues

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.

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!

Not using PREG_UNMATCHED_AS_NULL flag?

I noticed spatie/regex does not use the PREG_UNMATCHED_AS_NULL flag when it calls preg_match() and preg_match_all(). This results in inconsistent results with unmatched capturing groups.

Demo here.

I have created a pull request which fixes the issue and adds some tests. However, I wanted to verify two things:

  1. Is there a reason the flag is not being used?
  2. I noticed that MatchResult::group() uses isset() to determine whether or not a group exists. If we implement PREG_UNMATCHED_AS_NULL, that will return false for unmatched groups... which makes sense, but is also inconsistent. What is the desired behavior here?

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...

License confusion

Is this library MIT licensed, as per the LICENSE.md, or postcard-ware, as per the README.md?

It cannot be both because the postcard-ware places a restriction on usage (the requirement to send a postcard if used in production), while the MIT license declares that are no such restrictions other than maintaining the copyright notice.

update .gitattributes

Please, add:

.editorconfig
.gitattributes
.github 

to .gitattributes export-ignore

  • merge CONTRIBUTING.md with README.md

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.

Separate between single and multi replace?

I'm currently looking for a replacement for the built-in preg_match() and preg_replace() functions that is

  • better with error-handling
  • has better types

Your project ticks all the boxes except that Regex::replace(…)->result() returns array | string which triggers the same type-errors on phpstan and psalm as plain preg_replace() when simply assuming it must be a string, because the subject was a string.

My suggestion would be to distinguish between replace and replaceMulti and have them both return different ReplaceResults, one having ->result() return a string, the other an array of strings. Yes, this is mainly to make linters and autocompleters happy, but splitting a function that returns array | string into one that returns a string and one that returns an array makes it easier to work with.

If I implemented this and provided a PR for it, would you even consider it, or is this change too breaking for you or simply not fitting the goal?

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.

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

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.