GitHub repository: t-regx/T-Regx
Description: PHP regular expression brought up to modern standards.
Topics: high-level, pcre, php, prepared-patterns, regexps, regular-expressions

0.41.4 - Updated composer PHP constraint: tag_name: 0.41.4
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.41.4`! 

We've spent time on improving the performance of the library as well as handling of the input regular expressions. We've found another two bottlenecks and successfully fixed them.

Additionally, we're updating PHP constraint for composer packages, to include higher versions of PHP

The detailed list of changes is in [ChangeLog.md].

* Other
    * Relaxed composer PHP version requirement up.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0414
0.41.2 - Performance optimisations: tag_name: 0.41.2
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.41.2`! 

Another two weeks have passed, and this time we spent them on increasing the performance of library. There were a few obvious bottlenecks that we overlooked while developing T-Regx, mostly in the regular expression parser, horrible stuff, but we believe we managed to bring back up the standards now.

This release doesn't bring in any new features.

The detailed list of changes is in [ChangeLog.md].

* Bug fixes
    * Fixed a bug with improper parsing of character classes starting with `^]`.
    * Fixed a bug with improper client error handlers, when user-supplied callbacks threw exceptions.
* Performance
    * Increased pattern instantiating performance by parsing continuous literal figures as one entity.
    * Additional performance optimisations with usage of xDebug profiler and QCacheGrind.
    * Increased performance of parsing certain features by using PHP built-in functions, instead of loops.
    * Increased performance of prepared patterns by looking up strategies by hashtable.
    * Reimplemented some of internal parser logic without unnecessary conditions and iterations.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0412
0.41.1 - Support for Laravel and other frameworks: tag_name: 0.41.1
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.41.1`! 

We've been recently notified about a certain buggy behaviour, when T-Regx was used in an environment with overriden error handlers, which tends to be the case in Laravel applications, but also other applications. Needlessly to say, we've fixed the issue and T-Regx continues to work as before, even in an environment with overriden error handlers.

The detailed list of changes is in [ChangeLog.md].

* Features
    * Updated internal error handling, so T-Regx works in an environment with overriden error handler.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0411
0.41.0 - Added support for modifier 'n': tag_name: 0.41.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.41.0`! 

We've been working our fingers to the bone before official release of PHP 8.2, and with it comes the new and shiny `/n` modifier - "no auto capture". Some of us have been waiting for a long time, and in T-Regx we're not discriminating, and each PHP version should work the same. Because of that, we're backporting `/n` modifier to **every PHP version** supported. So if you're using T-Regx on PHP as old as 7.1, you are free to use `/n` in `Pattern` and `PcrePattern` to its fullest. It's completely supported. That feature is available as of version `0.41.0`.

The modifier `n` can be used on each PHP version (since T-Regx 0.41.0) seamlessly. That means, any usage of modifier `n` in any part of the library (including the regular expression itself) works out of the box, as if the library was running on PHP 8.2.

The detailed list of changes is in [ChangeLog.md].

* Features
    * Since `0.41.0` T-Regx officially supports PHP 8.2.
    * Added full support for `n` modifier for every supported PHP version: `7.1` - `8.2`
        * Despite modifier `/n` being only valid in vanilla regexp in PHP only since `8.2`,
          modifier `/n` is fully supported in T-Regx in each PHP version.

        * Added full support for `n` modifier in `Pattern`
            * `Pattern::of($pattern, 'n')` is now accepted
            * `Pattern::inject($pattern, $figures, 'n')` is now accepted
            * `Pattern::template($pattern, 'n')` is now accepted
            * `Pattern::builder($pattern, 'n')` is now accepted
            * `Pattern::literal($text, 'n')` is now accepted
            * `Pattern::alteration($texts, 'n')` is now accepted
            * `Pattern::mask($mask, $keywords, 'n')` is now accepted
            * `Pattern::list()` now accepts patterns with `n` modifier
        * Added full support for `n` modifier in `PcrePattern`
            * `PcrePattern::of("/$pattern/n", )` is now accepted
            * `PcrePattern::inject("/$pattern/n", $figures)` is now accepted
            * `PcrePattern::template("/$pattern/n")` is now accepted
            * `PcrePattern::builder("/$pattern/n")` is now accepted
    * Added public modifier constant to `Pattern`.
        * Added `Pattern::NO_AUTOCAPTURE`

          From now on, patterns can be constructed with such constants, for example:
          ```php
          Pattern::of('^(foo)$', Pattern::NO_AUTOCAPTURE)
          ```

          String literal modifiers continue to work as before
          ```php
          Pattern::of('^(foo)$', 'n');
          ```
      
The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0410
0.40.0 - Pattern modifier constants: tag_name: 0.40.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.40.0`! 

Release of `0.40.0` is an intermediate release - we're working our fingers to the bone before official release of PHP 8.2, and with it comes the new and shiny `/n` modifier - "no auto capture". Some of us have been waiting for a long time, and in T-Regx we're not discriminating, and each PHP version should work the same. Because of that, we're backporting `/n` modifier to **every PHP version** supported. So if you're using T-Regx on PHP as old as 7.1, you are free to use `/n` in `Pattern` and `PcrePattern` to its fullest. It's completely supported. That feature will be available in version `0.41.0`, which will be released in a couple of hours. But before that, we're releasing all changes that were made during the development of `/n` backport.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Removed `SubjectNotMatchedException.getSubject()`.
    * Moved `ChainedReplace`, `PatternTemplate` and  `TemplateBuilder` to other namespaces.
    * Updated `MalformedPatternException` message `Null byte in regex` to `Pattern may not contain null-byte`.
    * Updated `MalformedPatternException` message `...unexpected delimiter '\0'` to `...null-byte delimiter`.
* Bug fixes
    * Fixed a bug when using prepared patterns with `mask()` didn't take modifiers into account when running validation
        * Fixed the bug in `Pattern::mask()`
        * Fixed the bug in `Pattern::template()->mask()`
        * Fixed the bug in `Pattern::builder()->mask()`
        * Fixed the bug in `PcrePattern::template()->mask()`
        * Fixed the bug in `PcrePattern::builder()->mask()`
* Features
    * Added public modifier constants to `Pattern`.
        * Added `Pattern::CASE_INSENSITIVE`
        * Added `Pattern::UNICODE`
        * Added `Pattern::MULTILINE`
        * Added `Pattern::SINGLELINE`
        * Added `Pattern::IGNORE_WHITESPACE`
        * Added `Pattern::ANCHORED`
        * Added `Pattern::GREEDYNESS_INVERTED`
        * Added `Pattern::RESTRICTIVE_ESCAPE`
        * Added `Pattern::DUPLICATE_NAMES`
        * Added `Pattern::DOLLAR_ENDONLY`
        * Added `Pattern::STUDY`

      From now on, patterns can be constructed with such constants, for example:
      ```php
      Pattern::of('^foo$', Pattern::CASE_INSENSITIVE . Pattern::MULTILINE)
      ```

      String literal modifiers continue to work as before
      ```php
      Pattern::of('^foo$', 'im');
      ```
      
* Other
    * Exception `PlaceholderFigureException` now extend correctly implements `PatternException`.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0400
0.39.0 - Shouldn't all libraries be elegant and beautiful?: tag_name: 0.39.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.39.0`! 

Why PHP programmers tend to not filter arrays by regular expressions? Probably because `preg_filter()` doesn't filter arrays (it actually replaces, like `preg_replace()`), and the real function to filter arrays is `preg_grep()`, but who would know that? Yuk! Hence we're adding `Pattern.filter()` and `Pattern.reject()` methods, which accept `array`, and filter it - leaving of either subjects that match the pattern, or subejcts that don't match the pattern.

Additionally, we noticed that T-Regx users felt obligated to catch library exceptions, when using T-Regx API, but frankly, the intention of the library was never to practice exception-driven-development, so we changed all T-Regx public exceptions to `RuntimeException`, to emphasise that catching those exceptions isn't necessary.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Removed `Pattern.forArray().filter()`. Use `Pattern.filter()` instead.
    * Removed `Pattern.forArray().filterAssoc()`.
    * Removed `Pattern.forArray().filterByKeys()`.
    * Renamed `PatternList.chainedReplace()` to `PatternList.replace()`
* Bug fixes
    * Fixed a bug when using `PcrePattern` with delimiter `\n`, `\v`, `\f`, `t` didn't throw
      appropriate `MalformedPatternException`
* Features
    * Added `Pattern.filter()`, which filters subjects which match the pattern (exactly like previous `Pattern.forArray().filter()`).
    * Added `Pattern.reject()`, which filters subjects which do not match the pattern.
* Other
    * Public exceptions now extend `\RuntimeException`, instead of `\Exception`.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0390
0.38.0 - Revamped replacing interface: tag_name: 0.38.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.38.0`! 

Previous state of replacing API in T-Regx was, frankly, hopeless. It was full of chainable, complex interface, polymorphic instances, unhelpful names, reading count required passing an anonymous function to `counting()` - it was horrible. We've started working on a revamp 6 months ago, simulateneuously delivering other updates, and today we're proud to say the replacing API revamp is complete. We removed horrible `by()` function, it functionallity is now available through `callback()`/`withGroup()` methods. We removed `counting()` callback interface, for a more object-oriented `Replace.count()` call, which is performance optimized. That is, when `Replace.count()` is called on its own, it will perform a global search, counting values, but if `Replace.with()` (or other replacing function) is called, then its amount is used and further calls to `Replace.count()` will not perform additional calls. That way the user can freely use `Replace.count()` without worying about what calls will do what. Chainable interface was minimized to the absolute minimum - nothing is chainable, except for `limit(int)` method. There is also no polymorphic APIs, which greatly increase the readability of the API and its cohesion.

The newly revamped replacing API:

```php
/**
 * Instantiate your pattern
 */
$pattern = Pattern::of('\d+');

/**
 * Match the pattern against the subject
 */
$replace = $pattern->replace("My numbers are: 12, 13, 14");

/**
 * Replace occurrances of the pattern in the subject
 */
$replace->callback(fn(Detail $detail) => "X");

/**
 * Count the replacements performed
 */
echo "I replaced " . $replace->count() . " occurrences";
```

The detailed list of changes is in [ChangeLog.md].


* Breaking changes
    * Revamped `Pattern.replace()` functionality.
    * Removed `focus()` and `substitute()` - useful feature, however it proved to be unreliable when used with
      look-aheads, look-behinds and `\K` resets. The functionallity is available through a separate `"t-regx"` package.
    * Callback in `replace().callback()` no longer accepts `Detail` or `Group` as a return value.

Migration guide:

- Removed `focus()` - To continue using functinallity, use a separate package.
- Removed `Group.substitute()` - To continue using functinallity, use a separate package.
- Removed `replace().by()` - use `replace().callback()` or `replace().withGroup()`
- Removed `replace().counting()` - use `replace.count()`
- Removed `replace().all()` - omit call to `all()`
- Updated `replace().callback()` return type - return `Detail.text()` or `Group.text()` as a return value.
- Renamed `ReplaceLimit` to `Replace`.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0380
0.37.1 - More mature placeholders in prepared patterns: tag_name: 0.37.1
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.37.1`! 

This is an intermediate step into our long planned goal of T-Regx having UTF-8 enabled by default, out of the box. We are working on proper unicode and ascii group names, to continue supporting PCRE as well as PCRE2, and having `@` placeholder in group names isn't allowed. So we made a fix, so that proper `MalformedPatternException` is thrown, instead of `PlaceholderFigureException`.

The detailed list of changes is in [ChangeLog.md].

* Features
    * Add `PatternList.count()`, which allows to count occurrances of many patterns in a subject.
* Bug fixes
    * Corrected prepared patterns in malformed patterns.

      When malformed pattern is used, `foo(?<bar@cat>door)` (because of unallowed `@` character in the group name),
      previously T-Regx would treat `@` as a placeholder, and inject a figure into it. Currently, T-Regx
      ignores `@` character in the group name, so `PlaceholderFigureException` is not thrown, but
      `MalformedPatternException` is thrown instead.

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0370
0.37.0 - We're letting some things go: tag_name: 0.37.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.37.0`! We also would like to mention previous releases, `0.34.2`, `0.35.0` and `0.36.0`.

Most notably, we're making certain implementations of public interfaces as internal parts of the library. So for example, while `\TRegx\CleanRegex\Match\Detail` remains public, its implementations (`MatchDetail` and `LazyDetail`) are moved to internal namespace, and should not be used directly. They are still being passed to clients code, so you will see it for example with `get_class()`, but you should not rely on it. We're also deprecating `ReplaceDetail` and their exclusive methods like `modifiedSubject()` and other replacement-tracking methods. We also renamed certain methods, for example `flatMapAssoc()` to `toMap()`, and `Pattern.compose()` to `Pattern.list()`.

And by the way, we're working on a new, shiny version of t-regx.com, it will be super cool!

The detailed list of changes is in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0370
0.34.1 - New line conventions, PCRE (*VERB): tag_name: 0.34.1
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.34.1`!

Prepared patterns are a big part of T-Regx library, there actually isn't anything in PHP world (or regular expressions for that matter), that would handle user-data in patterns in such a way as T-Regx's Prepared Patterns with `@` placeholders. Among other features, the placeholders are correctly handled with `\@` and `\Q@\E`, as well as comments `#comment@\n` in Extended Mode (`x` flag). However, comments are only ended by `"\n"` when PCRE newline conventions are `(*LF)`. We're sorry to admit, that we didn't take that into account, that when you change line convention to `(*CR)` (or other convention for that matter), parsing of `@` placeholders in comments should be updated accordingly. As of this release, newline conventions are properly being taken into account. We're holding our statement, that `@` placeholders aren't being injected into comments.

The detailed list of changes is in [ChangeLog.md].

Summary of changes:
* Features
    * Prepared patterns now support changing new line conventions with flag `x` (`EXTENDED` mode).

      This code is now valid:
      ```php
      Pattern::inject("(*CR)#comment@\r@", ['value'], 'x');
      ```
      The first placeholder character `"@"` is considered a part of an extended comment, but the second
      placeholder `@` is assigned value `'value'`.

    * All types of PCRE newline conventions are supported: `(*LF)`, `(*CR)`, `(*CRLF)`, `(*ANYCRLF)`, `(*ANY)`, `(*NUL)`.

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0341
0.34.0 - Treating prepared patterns like adults: tag_name: 0.34.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.34.0`!

We noticed it would be better if we could treat placeholder `@` used in prepared patterns (`Patter::inject()` and `Pattern::template()`) as first-class citizens, for example put them in look-ahead's, or use `?` or `+` after them. Now, using `Pattern::inject('@?', ['Foo']);` works just fine. Any quantifier used at the placeholder `@` behaves as if applied to the injected value.

Additionally, we corrected usage of backtracking priorities in `Pattern::mask()`, so it's more predictible.

The detailed list of changes is in [ChangeLog.md].

Summary of changes:

* Breaking changes
    * Moved `PlaceholderFigureException` from `TRegx\CleanRegex\Internal\Prepared\Figure\ ` to `TRegx\CleanRegex\Exception\ `
* Features
    * Added support for quantifiers to placeholders in prepared patterns.
        * Added support for quantifiers in `Pattern::inject()`
        * Added support for quantifiers in `Pattern::template()`
        * Added support for quantifiers in `Pattern::builder()`
        * Added support for quantifiers in `PcrePattern::inject()`
        * Added support for quantifiers in `PcrePattern::template()`
        * Added support for quantifiers in `PcrePattern::builder()`
    * Added support for empty placeholders in prepared patterns:
      ```php
      Pattern::inject('Find:@?', ['']); // matches only "Find:"
      ```
    * Updated backtrakcing in prepared patterns.
        * Updated backtrakcing in `Pattern.pattern()`.

          ```php
          Pattern::template('@:Bar')->pattern('Foo:Bar|Foo'); // matches "Foo:Bar" or "Foo:Bar:Bar"
          ```

        * Updated backtrakcing in `Pattern.mask()`.

          ```php
          $template = Pattern::template('@:Bar');
          $template->mask('*', ['*' => 'Foo:Bar|Foo']); // matches "Foo:Bar" or "Foo:Bar:Bar"
          ```
* Other
    * Passing invalid arguments which also don't match the number of arguments in `Pattern::inject()`,
      now always prefers `\InvalidArgumentException` over `PlaceholderFigureException`.

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0330
0.33.0 - Matching and searching: tag_name: 0.33.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.33.0`!

We've started working on this change, wooo, before release of 0.27. We simultaneously were working on making the `Pattern.search()` happen, as well as adding new features to the library. We're happy we've finally made it.

It was bothering us that some methods of `Pattern.match()` were dealing with `Detail` (like `first()`, `findFirst()`, `stream()`) and others with `string` (like `all()`, `distinct()`, `reduce()`). We hated using that in production. It was not designed well, unpleasant, unelegant. We needed to make `Pattern.match()` more concise, so it would only work with `Detail`, but we also liked the fact that you could *simply* take a match and use it. To reconcile the two, we have split the matching into two methods: `Pattern.match()` and `Pattern.search()`. They have very similar set of methods, virtually all of the previous `Pattern.match()` methods, with slight difference.

Now, every method of `Pattern.match()` deals with `Detail`.

- `Pattern.match().all()`, `only()`, `filter()`, `distinct()` all return `Detail[]`
- `Pattern.match().first()`, `findFirst()`, `nth()`, `findNth()` return `Detail`
- Methods `groupByCallback()` and `reduce()` accept and return `Detail`.

On the other hand, new `Pattern.search()` deals only with `string`.

- `Pattern.match().all()`, `only()`, `filter()`, `distinct()` all return `string[]`
- `Pattern.match().first()`, `findFirst()`, `nth()`, `findNth()` return `string`
- Methods `groupByCallback()` and `reduce()` accept and return `string`.

The detailed list of changes is in [ChangeLog.md].

Summary of changes:

* Breaking changes
    * Removed previously deprecated `Pattern.match().group()`.
    * Removed `Pattern.match().asInt()`. Use `stream().asInt()` or `Detail.toInt()`.
    * `Pattern.match().first()` no longer accepts `callable` as its argument
    * `Pattern.match().findFirst()` no longer accepts `callable` as its argument
    * `Stream.first()` no longer accepts `callable` as its argument
    * `Stream.findFirst()` no longer accepts `callable` as its argument
    * Refactored `Pattern.match()` into `match()` and `search()`.

      `Pattern.match()` and `Pattern.search()` have virtually the same set of methods,
      with a slight difference. All of `Pattern.search()` operate on `string` which
      is the matched occurrence, and all of `Pattern.match()` methods now operate on
      `Detail`.
* Features
    * Added `Pattern.search()`, which is similar to `Pattern.match()`, but its methods
      only operate on `string`. From now on, `Pattern.match()` only operates on `Detail`.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0330
0.31.0 - Improvise, adapt, overcome: tag_name: 0.31.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.31.0`!

We have refactored T-Regx quite a bit, and there's more on the way. We renamed `Pattern.match()` methods, some `Detail` methods, unified streams and integer streams, unified optionals and more. What some call "creating breaking changes" we call "paying back the tech debt". 

The detailed list of changes is in [ChangeLog.md].

Summary of changes:

* Breaking changes
    * Refactored `Detail.groups()` and `Detail.namedGroups()`, so they return `Group[]`.
    * Renamed `Detail.hasGroup()` to `Detail.groupExists()`.
    * Renamed `Pattern.match().hasGroup()` to `Pattern.match().groupExists()`.
    * Removed *inline-groups*, `Pattern.match().group()`.
* Bug fixes
    * Calling `Detail.group().asInt()` with invalid integer base on an unmatched group, threw `GroupNotMatchedException`. Now it throws `InvalidArgumentException`.
    * Calling `Detail.group().toInt()` with invalid integer base on an unmatched group, threw `GroupNotMatchedException`. Now it throws `InvalidArgumentException`.
    * `Pattern.match().groupBy()` now correctly groups by duplicate name with `J` modifier.
    * Removed `Detail.usingDuplicateName()`.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0310
0.27.0 - Limits in splits, and why we hate optional arguments: tag_name: 0.27.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.27.0`!

This time we responded to couple of users using `array_slice(pattern()->split())` and other magic to mimic, what we think is an idea of limited splits and maybe `rsplit()` from Python. However `Pattern.split()` was always supposed to return all of the pieces being split, and frankly, we think that optional arguments in methods is a sign of bad design. Most suggestions were to the sound "just add `limit=-1` argument". We decided to go with a cleaner way. First of all, on the quest to defeat optional arguments, and second, because with a limit of cuts, we should decide where to limit them from - the start of the string or the end. Hence `splitStart()` and `splitEnd()` with `maxSplits` argument. The good, old `split()` continues to split all elements. We think it's a more elegant approach.

The detailed list of changes is in [ChangeLog.md].

Summary of changes:

* Breaking changes
    * Removed `Pattern.match().offsets()`. Use `Pattern.match().map()`.
    * Removed `Pattern.match().group().offsets()`. Use `Pattern.match().group().map()`.
    * When using `Pattern.split()`, previously unmatched separators were represented as an empty `string`. Now, they're represented as `null`.
    * Updated `flatMap()`, see details in [ChangeLog.md].
* Bug fixes
    * Fixed a bug with `tuple()` and `triple()`, see [ChangeLog.md].
* Features
    * Added `Pattern.splitStart()`, which works similarly to `Pattern.split()` but accepts a non-negative `maxSplits` argument, which can be used to limit splits from the start of the subject.
    * Added `Pattern.splitEnd()`, which works similarly to `Pattern.split()` but accepts a non-negative `maxSplits` argument, which can be used to limit splits from the end of the subject.
    * Added method `Optional.get()`. See details in [ChangeLog.md].
    * Added methods to `Pattern.match()`, see [ChangeLog.md].
* Deprecation
    * Deprecated `NotMatched` passed as `findFirst().orElse()` argument. Use `Pattern.match()` functions instead. Everything that `NotMatched` could be used for, can be got from `Pattern.match()`.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0270
0.25.0 - Quiet before the storm: tag_name: 0.25.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.25.0`!

Isn't it wierd that using `distinct()` causes `"0"` disappear, because there's `false` in the list as well? Yea, we hate it too. So we fixed it! We made `Stream` a little bit more like Java8 (yet again), fix nasty bugsies. We're working on the complete rewrite of the internal matching engine, so that T-Regx is a little bit less dependent on the underlying PCRE, but worry not, this will not change the API in any way. While we're working on the refactor, we find and fix stuff in the meantime, so now we decided to release it. After that we'll also refactor the replacing API, which will be quite drastic (a lot of the functions will need updating), but it will be similar. There will be a lot of time for migration.

The detailed list of changes is in [ChangeLog.md].


* Breaking changes
  * `Detail.group()` no longer implements `Optional`
  * `Detail.usingDuplicateName().group()` no longer implements `Optional`
  * Removed previously deprecated `Detail.group().orReturn()`. Use `or()` instead.
  * Removed previously deprecated `Detail.group().orElse()`
  * Removed previously deprecated `Detail.group().orThrow()`
  * Removed previously deprecated `ReplaceDetail.group().orReturn()`. Use `or()` instead.
  * Removed previously deprecated `ReplaceDetail.group().orElse()`
  * Removed previously deprecated `ReplaceDetail.group().orThrow()`
  * Updated how `Stream.distinct()` removes elements:
    * Now `'1'` and `true` are no longer considered equal
    * Now `''` and `false` are no longer considered equal
    * Now `0` and `false` are no longer considered equal
    * Now `1` and `true` are no longer considered equal
    * Now `0` and `'0'` are no longer considered equal
    * Now `null` and `false` are no longer considered equal

    For all intents and purposes, now `Stream.distinct()` works as-if it used strict-comparison `===`.
  * `Stream.filter()` no longer reindexes stream elements. To reindex them, chain the stream with `values()`.
      `match().filter()` still returns a sequential array with reindexed values.
  * Removed `Stream.only()`. Use `Stream.limit().all()` instead.
  * Removed `IntStream.only()`. Use `IntStream.limit().all()` instead.
  * Renamed `Group.textLength()` to `Group.length()`.
  * Renamed `Group.textByteLength()` to `Group.byteLength()`.
  * Renamed `Detail.textLength()` to `Detail.length()`.
  * Renamed `Detail.textByteLength()` to `Detail.byteLength()`.
* Bug fixes
    * Fixed a bug when using `Stream.values().keys().first()` didn't always reindex to `0`.
    * Fixed a bug when using regular groups in `stream()->asInt()` was allowed, but `usingDuplicateName()` groups weren't.
      Now both kinds of groups are correctly passed into `stream()->asInt()`.
    * Fixed a bug when using regular groups in `groupByCallback()` was allowed, but `usingDuplicateName()` groups
      weren't.
      Now both kinds of groups are correctly passed into `groupByCallback()`.
* Features
    * Added `Detail.group().or()` which behaves similarly to `orReturn()` but only accepts a non-nullable `string`.
    * Added `Stream.limit()`, which limits elements present in a stream from the end
    * Added `Stream.skip()`, which limits elements present in a stream from the start
    * Added `IntStream.limit()`, which limits elements present in a stream from the end
    * Added `IntStream.skip()`, which limits elements present in a stream from the start
* Other
    * Previously, using `Stream.keys().first()` return `0` for sequential arrays, and T-Regx didn't evaluate previous
      chains, such as `map()` or `flatMap()`. As of this release, they will be called for completeness,
      even though their results won't be used.


The detailed list of changes is also in [ChangeLog.md].

Rawrrrrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md#added-in-0250
0.23.0 - No more opaque stacktraces!: tag_name: 0.23.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.23.0`!

This version contains some changes that we were really looking forward to! First of all, hideous `findFirst()->orThrow(Exception::class);` is gone! We hated that decision, because instantiating exceptions like that hid the stacktrace - the exceptions were opaque, we trully hated using that in production, and were planning on updating that for a long time now. We also made T-Regx a little more robust, by dropping it's reliance on [`mb_internal_encoding()`]. Previously, you could actually change how T-Regx works by setting encoding other than UTF-8. Now, you can set [`mb_internal_encoding()`] all you want, and T-Regx works without changes. These two changes are marked as breaking changes, for the sake of semantic versioning, but in our eyes these are actually features (we bumped the `0.x` version because that's the recommendation of semver).

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Previously deprecated `Optional.orThrow()` accepted exception class name as `string`. Currently, `orThrow()`
      accepts an instance of [`\Throwable`].
    * Removed `ClassExpectedException`, which was thrown when an invalid class name was passed to `orThrow()`.
    * Removed `NoSuitableConstructorException`, which was thrown when an invalid class was passed to `orThrow()`.
    * Previously T-Regx used whatever encoding was set for [`mb_internal_encoding()`] for `Detail.offset()`/`tail()`
      /`textLength()`. Now, T-Regx always uses UTF-8 regardless of [`mb_internal_encoding()`]. For byte manipulation in
      encoding other than UTF-8 use `byteOffset()`/`byteTail()`/`byteTextLength()`.
    * Removed previously deprecated `Detail.setUserData()`/`getUserData()`
* Bug fixes
    * Fixed a bug when using `match()->groupByCallback()` didn't throw `InvalidReturnValueException` for invalid group
      value
* Deprecation
    * Deprecated `Detail.limit()`

[`\Throwable`]: https://www.php.net/manual/en/class.throwable.php

[`mb_internal_encoding()`]: https://www.php.net/manual/en/function.mb-internal-encoding.php


The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.21.0 - Small fixes, small steps, big ambitions: tag_name: 0.21.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.21.0`! This time we fixed a bug (yay!), and broke some stuff ;| We found that using `nth()` method which sometimes throws exception because of subject not matched, and sometimes because of missing items is hard to use. And, it's even not similar to other parts of library - for example `all()` doesn't throw exception when subject isn't matched. So we decided that `nth()` should treat all matches equally and not throw a dedicated exception when the subject wasn't matched. And we did the same for `stream()`. Worry not however, the messages remained the same, so you will know what's the reason for the exception - as a developer. But the API will be more uniform.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Renamed `GroupLimit` to `GroupMatch`.
    * Previously `nth()`/`findNth()` threw different exceptions when the subject wasn't matched and when the item was
      missing. Now they always throw `NoSuchNthElementException` (regardless of whether the subject was matched or not).
      Exception messages still remain, to inform you whether there was not enough occurrences or whether the subject
      wasn't matched.
        * `match()->nth()` throws `NoSuchNthElementException`, instead of `SubjectNotMatchedException`
        * `match()->asInt()->nth()` throws `NoSuchNthElementException`, instead of `NoSuchStreamElementException`
        * `match()->stream()->nth()` throws `NoSuchNthElementException`, instead of `NoSuchStreamElementException`
        * `match()->group()->nth()` throws `NoSuchNthElementException`, instead of `SubjectNotMatchedException`
        * `match()->group()->asInt()->nth()` throws `NoSuchNthElementException`, instead
          of `NoSuchStreamElementException`
        * `match()->group()->stream()->nth()` throws `NoSuchNthElementException`, instead
          of `NoSuchStreamElementException`
* Bug fixes
    * `match()->offsets()` and `match()->group()->offsets()` now return offsets as characters. Previously they returned
      them as bytes.
* Features
    * Added `SubjectNotMatchedException.getSubject()`


The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.20.2 - Some things come, some things go: tag_name: 0.20.2
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.20.2`! It's an immediate release, after a vulnerable `0.20.1`, which we are sorry to say, we accidentally broke support for PHP 7.1 (by using `str_contains()`), but it's fixed in `0.20.2`. There is a discussion explaining reasons for deprecation of `focus()` https://github.com/T-Regx/T-Regx/discussions/129 - before the `focus()` is removed from library, we'll release a separate package with the functionallity.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Removed `pattern()->match()->remaining()`, as it was a failed idea (in 0.20.0).
* Bug fixes
    * Fixed a bug when using `match()->groupBy()` for a nonexistent group didn't throw `NonexistentGroupException`.
    * Corrected a bug in prepared patterns, where closing group after in-pattern (`(?i)`) modifier didn't update the
      flags properly, resulting in improper parsing of placeholders in `Pattern::inject()`/`Pattern::template()`.
    * Corrected a bug when using modifier reset `(?^)` didn't properly unset extended pattern, leading to improper
      placeholder `@` parsing inside comments.
    * Fixed a bug when `stream()->map()->asInt()` ignored integer-base, assumed base 10.
* Features
    * Added `pattern->cut()` which splits string into exactly two parts.
    * Added `pattern()->match()->reduce()`.
    * Added `$flags` parameter to `preg::replace_callback()` which was added in PHP 7.4.
* Performance
    * Refactored internal PCRE parser, to minimise number of instances created.
    * Improved performance of `CompositePattern.chainedReplace()` by using single call to PCRE.
* Deprecation
    * Deprecated `Detail.substitute()`.
    * Deprecated `pattern()->replace()->focus()`.
* Other
    * Previously T-Regx removed duplicate flags before passing them to PCRE. Now flags are passed without modification.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.19.1 - Fixed improper prepared patterns with character classes.: tag_name: 0.19.1
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.19.1`!  It's a bug-fixing release. While working on our replacement revamp, we stumbled upon a bug writing our unit tests, believe that; so we decided we needed to fix this ASAP: https://github.com/T-Regx/T-Regx/issues/122

The detailed list of changes is in [ChangeLog.md].

* Bug fixes
    * Fixed a bug when pattern `[]]` with prepared pattern was parsed incorrectly.
    * Fixed a bug when using character classes (e.g. `[:alpha:]`) with prepared pattern were parsed incorrectly.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.19.0 - Adding Structure as counting() argument: tag_name: 0.19.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.19.0`!  It's a really small release, with the addition of a new argument for `replace()->counting()`.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Rename `BaseDetail` to `Structure`
* Features
    * Add second argument `Structure` as second parameter to `replace()->counting()`

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.18.0 - Preparation for the replace update: tag_name: 0.18.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.18.0`! That's just an intermediate version, that will allow you to upgrade more seamlessly.

We're in the middle of a general rewrite of the Replace Module (the `pattern()->replace()` part), and in the meantime, we have to rethink certain ideas. The sad part is, we must let certain methods go, like `otherwise()` - leaving room for more exciting new methods. So, for now, let's keep an open mind, to be ready for the next realease refreshed Replace Module. It won't change much, we promise! :)

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Removed `replace()->otherwise()`. Use `counting()` instead.
    * Removed `replace()->otherwiseReturning()`. Use `counting()` instead.
    * Removed `replace()->otherwiseThrowing()`. Use `counting()` or `atLeast()` instead.
* Other
    * T-Regx follows [Semantic Versioning 2](https://semver.org/)

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

v0.17.0 - We're ripping Java off: tag_name: 0.17.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.17.0`! We're coming to giga-big-release of `1.0.0` with big steps! 

We noticed that interface of `FluentMatchPattern` (result of `pattern()->match()->fluent()`) and its idea, conceptually, is similar to Java8 streams. Semi-functional programming approach, with terminal statements. We decided it was a good idea to make the name more descriptive, so that, if a dinosaur lover knows Java by some accident, he could find similarities here.

So we just renamed `FluentMatchPattern` to `Stream`. And also `fluent()` method to `stream()`.

The detailed list of changes is in [ChangeLog.md].

* Bug fixes
    * Fixed a bug when returning non-string value from `orElseCalling()` didn't throw `InvalidReturnValueException`
* Breaking changes
    * Renamed `FluentMatchPattern` to `Stream`, similar to Java 8 streams
    * Renamed `fluent()` to `stream()`, similar to Java 8 streams
    * Renamed `NoSuchElementFluentException` to `NoSuchStreamElementException`
* Features
    * Added `IntStream.stream()`

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

v0.16.0 - Refactor of asInt() and offsets(): tag_name: 0.16.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.16.0`! This time, we found out that by simplifying T-Regx internal implementation, we can actually simplify the public interface of the library as well! Cool, isn't it? :D

Currently, when you call `pattern()->match()->fluent()`, you receive `FluentMatchPattern` (something like `Stream` from Java8). However, when you call `pattern()->match()->asInt()` and `pattern()->match()->offsets()` you also receive `FluentMatchPattern`, which behaves slightly in a different way (for example throws different exceptions).

We stoped for a minute a thought - waidaminute, something's wrong, I can feel it. So we decided that what must be done is `fluent()` must be separated from `asInt()` and `offsets()`. That's how `IntStream` was born.

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * Methods `asInt()` and `offsets()` return `IntStream` instead of `FluentMatchPattern`.
    * Removed `FluentMatchPatternException`. In case of `asInt()`, `InvalidIntegerTypeException` is thrown instead.
    * Updated the rules when exceptions are thrown from `asInt()`, `offsets()` and `fluent()`:
        * Exceptions thrown from `IntStream`:
            * `pattern()->match()->asInt()` throws `SubjectNotMatchedException`
            * `pattern()->match()->offsets()` throws `SubjectNotMatchedException`
            * `pattern()->match()->group()->asInt()` throws `SubjectNotMatchedException` or `GroupNotMatchedException`
            * `pattern()->match()->group()->offsets()` throws `SubjectNotMatchedException` or `GroupNotMatchedException`
        * Exception thrown from `FluentMatchPattern`:
            * `pattern()->match()->fluent()` throws `NoSuchElementFluentException`
            * `pattern()->match()->asInt()->fluent()` throws `NoSuchElementFluentException`
            * `pattern()->match()->offsets()->fluent()` throws `NoSuchElementFluentException`

      Basically, `MatchPattern` and `IntStream` throw match-related exceptions (`SubjectNotMatchedException`
      or `GroupNotMatchedException`), whereas `FluentMatchPattern` throws fluent-related
      exception: `NoSuchElementFluentException`.
  * Updated exception messages from `asInt()`, `offsets()` and `fluent()`.
  * `MatchPatternInterface` is no longer part of T-Regx public API.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.15.0 - Templates and builders update!: tag_name: 0.15.0
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.15.0`! We're coming to giga-big-release of `1.0.0` with really big steps! 

This time, we look around our dinosaur friendly projects, and we noticed that most users use `Pattern::template()` with just one call, and need to call `build()` at the end. That's not very elegant, T-Regx the dinousaur said, and we agree! That's why we refactored. The API a little bit.

The detailed list of changes is also in [ChangeLog.md].

Previously, to create a template, you needed to call `Pattern::template()->something()->build()`. This is looong and inconvenient. Here's what we did.

* Breaking changes
    * Renamed `Pattern::template()` to `Pattern::builder()`. So if you need to build many elements, you can go
      ```php
      Pattern::builder($yourTemplateHere)
        ->literal($value)
        ->mask($mask, $keys)
        ->build();
      ```
* Features
    * Added `Pattern::template()`, which works similarly to `Pattern::builder()` but allows only one chain:
      ```php
      $pattern = Pattern::template('^@$')->literal('Hi!'); // that's it
      ```

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.14.0 - Quicky Pattern::compose() update: tag_name: 0.14.0
body: 😎 T-Regx The Dinosaur is proud to announce its release `0.14.0`! That's a quick release with the update of a few names regarding `Pattern::compose()`.  `allMatch()` wasn't a really good name, was it?

The detailed list of changes is also in [ChangeLog.md].

* Breaking changes
    * Name `Pattern::compose()->allMatch()` was horrible, we renamed it to `testAll()`
    * Name `Pattern::compose()->anyMatches()` wasn't good either, we renamed it to `testAny()`
  
    So, if you want to test a composite pattern, you just type `Pattern::compose()->test` and choose `Any()` or `All()` :)

    * Renamed `Pattern::compose()->chainedRemove()` to `prune()`, because "chained remove" was basically prune, just with a more scary name 😨 
* Features
    * Added `Pattern::compose()->failAny()`, returning `true` if any of the patterns didn't match the subject
    * Added `Pattern::compose()->failAll()`, returning `true` if all the patterns didn't match the subject

    Same as before, if you'd like to check if your composite pattern fails a subject, just type `Pattern::compose()->fail` and choose `Any()` or `All()` :)

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.13.8 - Fixed PHP reporting valid patterns as invalid: tag_name: 0.13.8
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.13.8`! We're coming to giga-big-release of `1.0.0` with big steps! 

In this release, we took a look at how PHP parses the patterns, before passing them to PCRE, and it shocked us! that it does so negligent. Perfectly valid patterns are reported as invalid by PHP, because of improper parsing.

Example:
- Pattern `/\c\/` is marked as invalid in PHP. So is `/\Q\/` and other valid patterns (like comment groups and comments in exnteded mode)

In this release we handled this bug, so that `pattern()`, `Pattern::of()`, `Pattern::mask()` and `Pattern::template()` correctly understand these patterns. We parse the whole pattern, and we guess if it would be reported by PHP, and we fill it with padding, to make it digest by PHP, while keeping the meritoric value. For example, T-Regx changes `\c\` to `\c\{1}` which is the same thing in regular expression, but PHP reports the first as invalid.

You, as a user of T-Regx don't have to worry about it at all. Simply use you regular expression, and let T-Regx handle it for you.

The detailed list of changes is in [ChangeLog.md].

* Bug fixes
    * Fixed a bug, where using `Pattern::inject('()(?)')` failed parsing
    * Fixed a bug, where using unicode in groups failed parsing
    * Fixed a bug, where using pattern in unclosed comment group failed parsing
    * Added workaround for PHP inconsistencies ragarding backslash in patterns:
        * PHP reports `\c\` as invalid entity, all T-Regx entry points correctly recognize it as valid
        * PHP reports `\Q\` as invalid entity, all T-Regx entry points correctly recognize it as valid
        * PHP reports `(?#\` as invalid entity, all T-Regx entry points correctly recognize it as valid
        * PHP reports `#\` as invalid entity in `X`tended mode, all T-Regx entry points correctly recognize it as valid
* Features
    * Added `Optional.map()`, which resembles Java 8 optionals.
    * `pattern()->match()->asInt()->findFirst()->orElse()` receive `NotMatched` argument
    * `pattern()->match()->asInt()->findNth()->orElse()` receive `NotMatched` argument
    * `pattern()->match()->offsets()->findFirst()->orElse()` receive `NotMatched` argument
    * `pattern()->match()->offsets()->findNth()->orElse()` receive `NotMatched` argument

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.13.5 - Refactor of pcre() API: tag_name: 0.13.5
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.13.5`! We're coming to giga-big-release of `1.0.0` with big steps! 

The detailed list of changes is in [ChangeLog.md].

* Breaking changes
    * We refactored `Pattern::pcre()`, so it's more concise, and doesn't use `builder()` method.
* Features
    * Added `Pattern::alteration()` which allows building `Pattern` with just an alteration group.
        * For example `Pattern::alteration(['foo', 'bar'])` is `/(?:foo|bar)/`
    * Added `Pattern::template()->alteration()`
* Bug fixes
    * Fixed a bug, where passing `false` as an alteration value didn't throw `\InvalidArgumentException`.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.13.4 - Integer bases: tag_name: 0.13.4
body: 😎 T-Regx The Dinosaur is really proud to announce its release `0.13.4`! We're coming to giga-big-release of `1.0.0` with big steps! From the last `0.12.0` release, we put more effort on rewriting the integer parsing, group building and more through exceptions in T-Regx.

Here's a summary of changes from `0.13.0`, `0.13.1`, `0.13.2`, `0.13.3` and in `0.13.4`.

The detailed list of changes is in [ChangeLog.md].

* Features:
  * Previously, `toInt()`/`asInt()`/`isInt()` assumed the numerical string was always in base 10, before mapping to `int`. Now, the base is passed in as an optional arugment (e.g. `asInt(2)`), and the default value of the argument is `10`. Allowed base are `2` - `36`, and allowed digits are `/[0-9a-z]/i` (depending on the base).
  * `toInt()`/`asInt()` now throw either `NumberFormatException` (as before), or `NumberOverflowException`, if number is well formatted, but exeeds integer size on a given architecture.
* Breaking chages:
  * We removed `match()->asArray()`, because `Detail.group()` and `Detail.namedGroup()` are better suited for this job.
  * `pattern()->forArray()` is now strict by default.
  * Removed `pattern()->remove()->all()`/`remove()->first()`, because removing single occurrance felt weird. Now use `pattern()->prune()`.
  * Previously `filter()` and `remaining()` would leave keys in the resulting array unchagnes (as if they treated `array` as dictionary/map). Now the keys are now re-indexed (as if they treat `array` as list). To make use of keys, use `flatMap()`/`flatMapAssoc()` or `groupBy()`/`groupByCallback()`.
* Bug fixes
    * We fixed a bug with `fluent()->first()`, where sometimes a silence false-negative with group being missing slipped by us. Exception `GroupNotMatched` was thrown, where in reallity `NonexistentGroup` should have been thorwn.
    * Fixed w bug where using Detail.usingDuplicateName() didn't throw NonexistentG
    * We fixed a bug with `match()->asInt()->keys()->first()` malformed integers didn't throw `NumberFormatException`
* We now created docker images to run our Github Actions against, in order to run tests on 32-bit architecture, as well as 64-bit (which was the only option). We did this, in order to make sure that integer parsing works well for both types of integer.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.12.0 - More careful placeholders in prepared patterns: tag_name: 0.12.0
body: 😎 T-Regx The Dinosaur is really proud to announce its twelfthversion! We found loose approach to `@` placeholders in prepared patterns, so we had to create a dedicated regular patterns parses that works with PCRE7, PCRE8, PCRE9 and PCRE10 instroduced in PHP8 and late PHP 7.4. Using the pattern, we can distinguish `@` as literal (where it can be injected with values), or in character class, quotes or comments (`[@]`, `\Q@\E` and `#@\n`, etc.) We also found that `bind()` method failed to fulfill its intented purpose, so it was removed from the library, however `template()` and `inject()` remain in the library. Also, also we also strictened the rules of `pattern()->forArray()`.

The detailed list of changes is in [ChangeLog.md].

* Features
    * We added internal regular expression parser, that's used when creating Prepared patterns. Now in-pattern
      structures can be properly recognized, eliminating cases of misuse. Most notablly `[@]`, `\Q@\E`, `\@`, `\c@` and
      others, like comment groups and comments in extended mode.
* Breaking changes
    * Prepared patterns now use internal regular expression parser, to determine what is a placeholder and what isn't:
        * Previously, `[@]` would be injected. Now it's treated as `"@"` character-class.
        * Previously, `\Q@\E` would be injected. Now it's treated as `@` literal.
        * Previously, `\c@` would be injected. Now it's `\c@` control character.
        * Previously, `#@\n` would be injected. Now, if `x` flag is used (globally, or as a subpattern), then it's
          treated as `@` comment.
        * Previously, `(?#@)` would be injected. Now it's treated as `@` comment.
        * Previously, `\@` would be treated as `@` literal. This remains unchanged.
    * Mask placeholders are no longer represented as `&` in templates, use `@`.
    * Refactored `Pattern::template()->builder()`. Use `Pattern::template()` now.
    * Removed `Pattern::bind()`. Use `Pattern::inject()` or `Pattern::template()->literal()`.
    * Removed `Pattern::prepare()`. Use `Pattern::inject()`.
    * Removed `Pattern::pcre()->bind()`.
    * Removed `Pattern::pcre()->prepare()`.
    * Removed `Pattern::template()->bind()`.
* Bug fixes
    * Correct type-error in `ValidPattern.isValid()` on PHP 8.1.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.11.0 - Updates to templates, interfaces and builders: tag_name: 0.11.0
body: 😎 T-Regx The Dinosaur is really proud to announce its eleventh version! We simplified interface of templates and builders, as well as unified `Pattern`/`PatternImpl` and `PatternInterface`. We also added `Pcre` facade, allowing to read the exact version of PCRE used, regardless of PHP version.

The detailed list of changes is in [ChangeLog.md].

- Features:
  * Added `Detail.usingDuplicateName().get()` #101
  * Added `Detail.usingDuplicateName().matched()` #101
  * Method `Pattern:template()->literal(string)` now accepts string argument, allowing for inserting arbitrary strings into the pattern.
  * Added `Pattern::builder()`, which works similarly to how `PatternBuilder::builder()` worked.
  * Added `Pattern::literal()` which creates an instance of a pattern with which matches an arbitrary string exactly, even when `x` (`EXTENDED`) flag is used. To add in-pattern structures, like `^` or `$`, use `Pattern::template()->literal()`.
  * Added `Pattern::template()->literal()`, which is a shorthand for `Pattern::template()->builder()->literal()->build()`.
  * Added `Pattern::template()->mask()`, which is a shorthand for `Pattern::template()->builder()->mask()->build()`.
  * Casting `PatternInterface` to string results in a delimited pattern
  * Add `Pcre` version helper
- Breaking changes:
  * `match()->getIterator()` no longer preserves the keys of values (like `all()`)
  * `match()->group()->getIterator()` no longer preserves the keys of values (like `all()`)
  * Renamed `Pattern::format()` to `Pattern::mask()`
  * Renamed `Pattern::builder()->format()` to `Pattern::builder()->mask()`
  * Renamed `Pattern::template()->format()` to `Pattern::template()->mask()`
  * Refactored `Pattern::template()->formatting()` to `Pattern::template()->builder()->mask()`
  * Method `literal()` now requires argument `'&'`, to escape `&` in-pattern token
  * Removed `PatternBuilder::builder()`. Use `Pattern::builder()`
  * Removed `PatternBuilder::compose()`. Use `Pattern::compose()`
  * Renamed `FormatMalformedPatternException` to `MaskMalformedPatternException`
  * Removed interface `PatternInterface`. Now class `Pattern` is both an instance of a pattern, as well as a static-factory, i.e. `Pattern::of()`/`Pattern::inject()`.
- Bug fixes
  * `Pattern::template()` quoted values incorrectly, when delimiter other than `/` or `%` was chosen.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.10.2 - Implicit replace all() and fixes: tag_name: 0.10.2
body: 😎 T-Regx The Dinosaur is really proud to announce its tenth beta version! This time we fixed a lot of bugs and mistakes, and we added implicit `all()` with replacing.

The detailed list of changes is in [ChangeLog.md].

### Summary
* Breaking changes
    * Rename `DetailGroup` to `Group`
    * Rename `ReplaceDetailGroup` to `ReplaceGroup`
    * Rename `BaseDetailGroup` to `CapturingGroup`
* Features
    * Calling `pattern()->replace()` without `all()`/`first()`/`only()`, implicitly assumes `all()`
* Bug fixes
    * Group name `"group\n"` used to be considered valid, now it's correctly being treated as invalid.
* Other
    * [`ReplaceMatch`] is now a class, not an interface.
    * When invalid strings, error messages will now also print invisible characters, for
      example `"Foo\n"`, instead of 
      ```
      "Foo      
      "
      ```
    * Update messages and exceptions thrown in edge-cases from `group()->fluent()` #93


The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[`ReplaceMatch`]: https://t-regx.com/docs/replace-match-details
[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md

0.10.1 - Fixing filtering: tag_name: 0.10.1
body: 😎 T-Regx The Dinosaur is really proud to announce its ninth beta version! This time we fixed filtering items in regular matching as well as `fluent()`/`asInt()`/`asArray()` chains.

The detailed list of changes is in [ChangeLog.md].

### Summary
* Breaking changes
    * Chainable `pattern()->match()->filter()` is renamed to `remaining()`.
    * After filtering `MatchPattern` with `remaining()`, consecutive `Detail.index()` will no longer be reindexed, they will preserve the `index()` they had had before `remaining()`.
    * `match()->fluent()->filter()` no longer reindexes values. To reindex, use `values()`.
* Bug fixes
    * Fixed a bug where `fluent()->flatMap()->first()` would return the `array`, instead of the first element
* Features
    * Add `pattern()->match()->filter()` which returns only matches allowed by the predicate.
    * Add `pattern()->match()->group()->asInt()`
* Other
    * `pattern()->match()->fluent()->filter()->first()` first calls [`preg_match()`], and if that result doesn't match
      the predicate, then it calls [`preg_match_all()`].

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[`Match`]: https://t-regx.com/docs/match-details
[`preg_match()`]: https://www.php.net/manual/en/function.preg-match.php
[`preg_match_all()`]: https://www.php.net/manual/en/function.preg-match-all.php
[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.10.0 - T-Regx on PHP8: tag_name: 0.10.0
body: 😎 T-Regx The Dinosaur is really proud to announce its eighth beta version! Despite the beta suffix, it's 100% suitable for production use. This time, we finally add support for PHP8. It's not just updating the `dev-dependencies` versions. Argument errors and exceptions in PHP change with version PHP8, we needed to handle them properly, so the API of T-Regx stays the same. We would hate for T-Regx users to see irregularities when they update their PHP versions from 7.4 to 8.0.

The detailed list of changes is in [ChangeLog.md].

### Summary
* Breaking changes
    * Previously deprecated [`Match`] is removed from T-Regx. Please, do use [`Detail`] which was added two versions ago. 
* Other
    * T-Regx supports PHP 8.

The detailed list of changes is also in [ChangeLog.md].

Rawrrrr!

[`Match`]: https://t-regx.com/docs/match-details
[`Detail`]: https://t-regx.com/docs/match-details
[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.9.14 - Formats and expectations!: tag_name: 0.9.14
body: 😎 T-Regx The Dinosaur is really proud to announce its seventh beta version! Despite the beta suffix, it's 100% suitable for production use. This time, we bring replacements excpectations, which allow you to declare the wanted replacements performed and formats, which lets you simplify patterns for your users to create psueudo-patterns or meta-patterns if you will.

The formats are a way of building patterns, so that you can assign tokens to regular expressions; and allow an arbitrary string (say, user input) to be used to build the final pattern, using the regular expressions.

1. Tell your user he can use tokens, like so:
    > Dear user. Type a mask, where `%d` is a digit, and `%w` is letter, `%ws` to include whitespace, `%%` to include `%` sign.
2. User sends the mask `['mask' => '(search:%w,age:%d)']`
3. Using the mask
    ```php
    $mask = $_GET['mask'];
    ```
4. Using the mask, build a pattern (which has the same interface as `Pattern::of()` and `Pattern::inject()`)
    ```php
    Pattern::format($mask, [
      '%d'  => '\d+',
      '%w'  => '[a-z]+',
      '%ws' => '\s+',
      '%%'  => '%'
    ]);
    ```

That would be the equivalent of `Pattern::of('\\(search:[a-z]+,age:\d+\\)')`. Characters in `$mask` are treated utterly as text (totally quoted), and only the patterns on the right-hand side of the tokens are treated as regular expressions.

## About the changes:

The detailed list of changes is in [ChangeLog.md], and here's a summary:

* Breaking changes
    * Rename `DetailGroup.replace()` to `DetailGroup.substitute()`
    * Rename `match().groupBy().texts()` to `match().groupBy().all()`
    * Rename exceptions
        * Rename `Utf8OffsetPregException` to `UnicodeOffsetException`
        * Rename `SubjectEncodingPregException` to `SubjectEncodingException`
        * Rename `CatastrophicBacktrackingPregException` to `CatastrophicBacktrackingException`
        * Rename `RecursionLimitPregException` to `RecursionException`
        * Rename `JitStackLimitPregException` to `JitStackLimitException`
* Bug fixes
    * Using pattern with a trailing backslash (e.g. `"(hello)\\"`) would throw
      `MalformedPatternException` with a really weird message, exposing the implementation details. Now the message
      is `Pattern may not end with a trailing backslash`.
    * Fix an error where optionals didn't work properly for `match()->offsets()->fluent()`
    * Fix an error where `ReplaceDetail` would return malformed `modifiedSubject()` for utf-8 replacements

* Features
    * Add pattern formats and pattern templates, a new way of creating pseudo-patterns for user supplied data
    * Add `match()->flatMapAssoc()` #88
    * Add `replace()->counting()`, invoking a callback with the number of replacements performed #90
    * Add `replace()->exactly()`, validating that exactly this many replacements were performed #90
    * Add `replace()->atLeast()`, validating that at least this many replacements were performed #90
    * Add `replace()->atMost()`, validating that at most this many replacements were performed #90
    * Add `pattern()->prune()` which removes every occurrence of a pattern from subject (identical to `remove()->all()`)
* Other:
    * Added interface `PatternStructureException` which can be used to catch exceptions for errors solely in pattern
      structure (recursion, backtracking, jit limit).

The detailed list of changes is in [ChangeLog.md].

See ya later, aligator! 

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
[`preg_match()`]: https://www.php.net/manual/en/function.preg-match.php
[`preg_last_error()`]: https://www.php.net/manual/en/function.preg-last-error.php
[`PREG_INTERNAL_ERROR`]: https://www.php.net/manual/en/pcre.constants.php
[`\InvalidArgumentException`]: https://www.php.net/manual/en/class.invalidargumentexception.php
[`get_class()`]: https://www.php.net/manual/en/function.get-class.php

0.9.13 - Exceptions, errors and gotchas: tag_name: 0.9.13
body: 😎 T-Regx The Dinosaur is really proud to announce its sixth beta version! Despite the beta suffix, it's 100% suitable for production use. This time, we added another higher-level API in replacing; proper handling of duplicate named groups (with `/J` modifier), more methods to T-Regx-specific exceptions, PHP gotchas handling and preparation for PHP 8. 

The detailed list of changes is in [ChangeLog.md].

### Summary
* Breaking changes
    * None! Really
* Deprecation
    * Deprecate [`Match`], use [`Detail`] instead.

      In preparation for PHP 8, in which `match` is a new keyword,
      we deprecate [`Match`] and `ReplaceMatch`. [`Match`] will become an invalid
      class name in PHP 8. 

* Features
    * Add `pattern()->replace()->focus(group)` #82
  
      Allows you to "focus" on a certain group while replacing, so only that one particular group will be replaced. In the future, there are plans for "focus" method that can replace an arbitrary list of groups.

    * Added proper handling of `/J` flag #84
The detailed list of changes is also in [ChangeLog.md].

* Other
    * Updated some exceptions' messages format; most notably, indexed groups as formatted as `#2`, 
      and named groups as `'group'`.
* SafeRegex
    * After calling [`preg_match()`] with overflowing offset, [`preg_last_error()`] would return
     [`PREG_INTERNAL_ERROR`], which T-Regx would handle, throwing `RuntimePregException` with proper message. 
      Negative offsets would be ignored.

      Since now, T-Regx throws [`\InvalidArgumentException`] in both cases.

See ya later, aligator! 

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
[`preg_match()`]: https://www.php.net/manual/en/function.preg-match.php
[`preg_last_error()`]: https://www.php.net/manual/en/function.preg-last-error.php
[`PREG_INTERNAL_ERROR`]: https://www.php.net/manual/en/pcre.constants.php
[`\InvalidArgumentException`]: https://www.php.net/manual/en/class.invalidargumentexception.php
[`Match`]: https://t-regx.com/docs/match-details
[`Detail`]: https://t-regx.com/docs/match-details
0.9.12 - Oopsie fix: tag_name: 0.9.12
body: 😢  T-Regx The Dinosaur is sad to announce that we've introduced a bug in 0.9.11 version (`TypeError` when no argument was passed to `group()->orThrow()`).

It's not a big deal, but it's something we should've caught before the release. Our bad!

### Summary

* Bug fixes
    * Fixed an occasional `TypeError` (Bug introduced in 0.9.11, fixed in 0.9.12)

This version of course is listed as a change in [ChangeLog.md].

See ya later, aligator! 

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.9.9 - Security and technical update, no biggie: tag_name: 0.9.9
body: 😎 T-Regx The Dinosaur is really proud to announce its fifth beta version! Despite the beta suffix, it's 100% suitable for production use. This time, we added some security fixed and performed some throughout internal changes to prepare ourselves for the big changes, later down the track.

The detailed list of changes is in [ChangeLog.md].

### Security fixes

 - We handled the PHP bug [#77827](https://bugs.php.net/bug.php?id=77827) - if you use T-Regx API, you don't have to worry about it :) it's handled under the hood.
 - We added support for `x` - extended flag in [prepared patterns]
 - We saw a lot of people using `preg_quote()` incorrectly (passing more than one character to it), so now `preg::quote()` throws `\InvalidArgumentException` for more than one character passed to `preg::quote()`.

### Summary
* Breaking changes
    * Renamed `pattern()->delimiter()` to `pattern()->delimited()`
* Features
    * Add `MatchGroup.equals()`, that allows to compare a potentially unmatched group with a string. 
    * Add `pattern()->match()->group()->filter()` method. #22
    * Add `pattern()->replace()->by()->mapAndCallback()`, which first translates a match by a dictionary (like `map()`), and then passes it through callback, before replacing (like `callback()`).
* Bug fixes
    * Fixed a bug in [Prepared patterns] (PCRE mode), when using a malformed pattern caused `TypeError`,
     instead of `MalformedPatternException`. 

The detailed list of changes is also in [ChangeLog.md].

See ya later, aligator! 

[prepared patterns]: https://t-regx.com/docs/handling-user-input
[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.9.7 - Match as vanilla array!: tag_name: 0.9.7
body: 😎 T-Regx The Dinosaur is really happy to announce its fourth beta version! Despite the beta suffix, it's 100% suitable for production use. This time, here comes some bug fixes and a slightly less T-Regx'y, more PHP-y way of accessing matches.

The detailed list of changes is in [ChangeLog.md].

## What about the matches

In vanilla regexps, after your `preg_match()`, you would recieve `$match` array of `string` of the whole match and capturing groups (or `[string,int]` if you used `PREG_OFFSET_CAPTURE`). 

Except for the fact of bizarre design of `preg_match()`, which **trims** every empty or unmatched capturing group away. If the empty/unmatched capturing group is not at the tail of `$match` then it's either `''` or `null` if you used `PREG_UNMATCHED_AS_NULL` (PHP 7.2 only).

So, obviously, T-Regx can't let you use such black magic :D But we still understand that often PHP-y way is more concise:
```php
$match['unit'];                 # this one sparks joy
$match->group('unit')->text();  # this one sparks less joy
```

So, we created `asArray()` method, used like so:
```php
$subject = "Foo:14";
pattern('\w+(?<number>:\d+)?(-\d+)?')->match($subject)->asArray()->first(function (array $match) {
    var_dump($match);
});
```
```
['Foo:14-16', 'number' => ':14', ':14', null]
```
Notice, that after `asArray()`, `first()` callback recieves `array`, instead of `Match`. The array structure resembles the vanilla `$match` structure, except for the fact that every unmatched group is represented as `null` (even for PHP 7.1 and earlier), and the trailing groups are never trimmed.

## Why not make `Match` implement `ArrayAccess` and use array syntax?

Where-as `ArrayAccess` does make it possible to read/check values of array (`$match[0]` and `isset($match[0])`), which would render `asArray()` method unnecessary - it does come at a price, that is:
 - `ArrayAccess` enforces `unset` and `add` methods, yet we we don't wish to modify the `Match` details.
 - Can't use `array_key_exists` on an object, even with `ArrayAccess`
 - Can't cast it to `array` (can't use `Match` with `array` type-hint)
 - No method that works for arrays would work for `Match` (such as `array_push()`, `array_map()`)
 - `$match[]` would still not work
 - We could not throw exceptions to validate groups, for example one could use `isset($match[-2])` and no error could be thrown, obviously.

But these are minor, language-specific matters. The real reason we didn't use `ArrayAccess` are:

 - `empty($match[0])` would return `true` for match `"0"` (string) !!
 - `$match['100']` is passed to `ArrayAccess.offsetGet()` as `100` (integer) !! so it's impossible to validate a malformed group consistently. 

Hence, we chose `asArray()` to return a real `array` for which all of those flaws go away :)

The rest of the changes is in [ChangeLog.md].

### Thanks

That's it for today! The 1.0.0 release is coming! :)

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/develop/ChangeLog.md
0.9.6 - First/all changes!: tag_name: 0.9.6
body: 😎 T-Regx The Dinosaur is really proud to announce its third beta version! Despite the beta suffix, it's 100% suitable for production use. This time, here come bug fixes, exception message updates, and two chaining-methods updates.

The detailed list of changes is in [ChangeLog.md].

### Overview

At T-Regx, we utilize chainable interface to increase readability and extensibility, for example:
```php
<?php
pattern($p)->match($subject)->group('name')->offsets()->findFirst($callable)->orThrow(Exception::class);
```
(not to mention versions with `fluent()`).

Anyway, previously, only immediate `first()` call (`pattern()->match()->first()`) used `preg_match()`, any intermediate calls (`pattern()->match()->offsets()->first()`, `group()->first()`, etc.) used `preg_match_all()` and trimmed the result.

It wasn't easy, but in this release, every single usage of `first()`/`findFirst()` uses `preg_match()` under-the-hood! That can save us from, say, catastrophic backtracking in certain scenarios. The only exception is `filter()->first()`, for which it doesn't make much sense to call `preg_match()`.

### Summary

* Breaking changes
    * `pattern()->match()->fluent()->distinct()` will no longer re-index elements (will not remove keys).
      - To re-index keys, use `distinct()->values()`.
      - `pattern()->match()->distinct()` still re-indexes keys.
* Enhancements 🔥
    * Every `match()->...()->first()` method calls `preg_match()`, instead of `preg_match_all()`. More below.
* Features
    * Added `pattern()->match()->fluent()->nth(int)` used to get an element based on an ordinal number.
    * Added `pattern()->match()->asInt()`. More below.

### Thanks

That's it today! The 1.0.0 release is coming! :)

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/master/ChangeLog.md

0.9.5 - Alternation in prepared patterns!: tag_name: 0.9.5
body: 😎 T-Regx The Dinosaur is really proud to announce its second beta version! Despite the beta suffix, it's 100% suitable for production use. It doesn't have any known bugs - check out the issues. There is a few breaking changes (since that's a 0.* version), but there are also improvements and new feautres.

The detailed list of changes is in [ChangeLog.md].

Here's a summary:

* Breaking changes
    * Removed `iterate()` method, which was only used as a substitute for `forEach()`, pre PHP 7.0, when
      keywords couldn't be used as method names.
    * Renamed `pattern()->match()->forFirst()` to `findFirst()` #70

* Features
   * Added `match()->group()->findFirst()` #22 #70
   * Added alternating groups in prepared patterns 🔥
       - `Pattern::bind()`, `Pattern::inject()` and `Pattern::prepare()` can now accept `string[]`, which will be treated as a regex alternation group!
         ```php
         Pattern::bind('Choice: @values', [
             'values' => ['apple?', '[orange]', 'pear']
         ]);
         ```
         is similar to
         ```php
         Pattern::of('Choice: (apple\?|\[orange\]|pear)')
         ```
         The alternation is also optimized for `i` and `u` flags, for example it will remove case-insensitive duplicates (like `foo` and `FOO` when `i` flag is used).

That's it for this release, stay tuned!

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/master/ChangeLog.md

0.9.4 - Exception changes and groupBy!: tag_name: 0.9.4
body: What's new, new, new?

This release brings updates in exceptions (namespaces, new detailed exceptions) and a `groupBy()` method.

## Exceptions

In previous realase we renamed `SafeRegexException` to `PregException`. In this, we're renaming `CleanRegexException` to `PatternException`. So now, those two general exceptions sync nicely with their base methods:

```php
try {
  return preg::match('/Foo/', $subject);
} catch (PregException $e) {
```

```php
try {
  return pattern('Foo')->test($subject);
} catch (PatternException $e) {
```

They both extend `RegexException` - base for all exceptions thrown by T-Regx. So that's the first thing.

The second exception update - previously, every exception thrown based on `preg_last_error()` method was `RuntimePregException`. Now, each error has a dedicated exception, which can be caught separately:
```php
try {
  return preg::match($pattern, $subject);
} catch (BacktrackLimitPregException $exception) {
} catch (Utf8OffsetPregException $exception) {
```
The detailed list of changes is in [ChangeLog.md].

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/master/ChangeLog.md

## `groupBy()`

This realase also comes with a brand new method - `groupBy()` which groups matches by a capturing group (name or index). It can match strings, offsets and also map them with `map()` and `flatMap()`. Additionally, it can be chained with `filter()` to leave out unwanted matches:
```php
return pattern('(\d)(?<unit>cm|mm)')->match($strings)
  ->filter(function (Match $match) {
    return $match->group(1)->toInt() % 2 == 0;
  })
  ->groupBy('unit')
  ->map(function (Match $match) {
    return $match->group(1)->toInt() * 100;
  });
```
0.9.2 - You're in for a treat: tag_name: 0.9.2
body: 😎 T-Regx The Dinosaur is really proud to announce its first beta version! Despite the beta suffix, it's 100% suitable for production use. It doesn't have any known bugs - check out the issues. There is a few breaking changes (since that's a 0.* version), but there are also a looot of improvements and new feautres.

The detailed list of changes is in [ChangeLog.md]. 

Here's a summary:

* Breaking changes
    * Refactored entry points - `Pattern::of()`/`pattern()` and `Pattern::pcre()` (see more in ChangeLog.md)
    * Renamed `parseInt()` to `toInt()`
    * Removed `pattern()->match()->test()`/`fails()`. From now on, use `pattern()->test()`/`fails()`
    * Removed `is()` (see more in [ChangeLog.md])
    * Refactored `split()` (see more in [ChangeLog.md])
* Features
    * Added `Match.group().replace()` 🔥
    * Added to `pattern()->match()`: 
       - `fluent()`,
        `asInt()`, 
       `distinct()`, 
       `groups()`, 
       `offsets()->fluent()`, 
       `group(string)->offsets()->fluent()`
    * Added `pattern()->forArray()->strict()` which throws for invalid values
    * Added `Pattern::inject()`/`Pattern::bind()`
* SafeRegex
    * Added `preg::grep_keys()` 🔥
* Other
    * Now `MalformedPatternException` is thrown, instead of `CompileSafeRegexException`, when using invalid PCRE syntax.

That's just a handful, the more detailed description of this realease is in [ChangeLog.md].

[ChangeLog.md]: https://github.com/T-Regx/T-Regx/blob/master/ChangeLog.md
0.9.1: tag_name: 0.9.1
body: :sunglasses: T-Regx The Dinosaur is proud to announce its second alpha version! It doesn't have any known bugs - it's just missing a few time-consuming features.

What's new in this release:
 - UTF8-safe method `textLength()` for `Match` and `Match.group()`
 - `groupsCount()` method for `Match` details - unsurprisingly just counts the groups (without duplicates of named and regular groups)

And more changes, that required a large part of replacing core to be rewritten, to allow more control over unmatched groups:
 - Methods `by()->group()->orIgnore()` and `by()->group()->orElse()`
 - Method `by()->group()->callback()` which accepts `MatchGroup` as an argument
 - Method `by()->group()->orElse()` now receives lazy-loaded `Match`, instead of a less useful - subject

The new features are already described in the documentation at [t-regx.com](https://t-regx.com/docs/replace-by-group) :)