- Soon
- Deprecation
- Deprecate
Pattern
methods unqualified for release candidate.- Deprecate
Pattern.fails()
- Deprecate
Pattern.search()
- Deprecate
Pattern.prune()
- Deprecate
Pattern.cut()
- Deprecate
Pattern.valid()
- Deprecate
Pattern::STUDY
- Deprecate
Pattern::RESTRICTIVE_ESCAPE
- Deprecate
Pattern::DOLLAR_ENDONLY
- Deprecate
Search
- Deprecate
- Deprecate
Matcher
methods unqualified for release candidate.- Deprecate
Matcher.fails()
- Deprecate
Matcher.findFirst()
- Deprecate
Matcher.only()
- Deprecate
Matcher.nth()
- Deprecate
Matcher.forEach()
- Deprecate
Matcher.map()
- Deprecate
Matcher.filter()
- Deprecate
Matcher.flatMap()
- Deprecate
Matcher.toMap()
- Deprecate
Matcher.distinct()
- Deprecate
Matcher.stream()
- Deprecate
Matcher.groupBy()
- Deprecate
Matcher.groupByCallback()
- Deprecate
Matcher.reduce()
- Deprecate
Matcher.subject()
- Deprecate
Matcher.groupNames()
- Deprecate
Matcher.groupsCount()
- Deprecate
Matcher.groupExists()
- Deprecate
- Deprecate
Detail
methods unqualified for release candidate.- Deprecate
Detail.get()
, to be renamed to.group()
- Deprecate
Detail.group()
, to be refactored to returnstring
- Deprecate
Detail.groups()
- Deprecate
Detail.namedGroups()
- Deprecate
Detail.matched()
, to be renamed to.groupMatched()
- Deprecate
Detail.all()
- Deprecate
Detail.groupNames()
, to be refactored toPattern.groupNames()
- Deprecate
Detail.groupsCount()
, to be refactored toPattern.groupsCount()
- Deprecate
Detail.length()
- Deprecate
Detail.offset()
, to be renamed to.start()
- Deprecate
Detail.tail()
, to be renamed to.end()
- Deprecate
Detail.byteLength()
- Deprecate
Detail.byteOffset()
, to be renamed to.byteStart()
- Deprecate
Detail.byteTail()
, to be renamed to.byteEnd()
- Deprecate
Detail.byteLength()
- Deprecate
Detail.toInt()
- Deprecate
Detail.isInt()
- Deprecate
Intable
Detail
is to be refactored from an interface to a class
- Deprecate
- Deprecate
Group
, as unqualified for release candidate.- Deprecate
Group
, useDetail
methods instead - Deprecate
Group.text()
, to be refactored toDetail.group()
- Deprecate
Group.isInt()
- Deprecate
Group.toInt()
- Deprecate
Group.matched()
, to be refactored toDetail.groupMatched()
- Deprecate
Group.equals()
, useDetail.groupOrNull()
instead - Deprecate
Group.or()
, to be refactored toDetail.groupOrNull()
- Deprecate
Group.index()
, usePattern.groupNames()
to calculate index based on name - Deprecate
Group.name()
, usePattern.groupNames()
to calculate name based on index - Deprecate
Group.usedIdentifier()
- Deprecate
Group.all()
- Deprecate
Group.subject()
- Deprecate
Group.offset()
, to be refactored toDetail.groupStart()
- Deprecate
Group.byteOffset()
, to be refactored toDetail.groupByteStart()
- Deprecate
Group.tail()
, to be refactored toDetail.groupEnd()
- Deprecate
Group.byteTail()
, to be refactored toDetail.groupByteEnd()
- Deprecate
Element
interface - Deprecate
Structure
interface
- Deprecate
- Deprecate
Replace
methods unqualified for release candidate.- Deprecate
Pattern.replace().with()
, to be refactored toPattern.replace()
- Deprecate
Pattern.replace().withGroup()
, to be refactored toPattern.replaceGroup()
- Deprecate
Pattern.replace().withReferences()
- Deprecate
Pattern.replace().callback()
, to be refactored toPattern.replaceCallback()
- Deprecate
Pattern.replace().count()
, to be refactored toPattern.replaceCount()
- Deprecate
Pattern.replace().exactly()
- Deprecate
Pattern.replace().atMost()
- Deprecate
Pattern.replace().atLeast()
- Deprecate
Pattern.replace().first()
, use$limit
argument in replace methods - Deprecate
Pattern.replace().limit()
, use$limit
argument in replace methods
- Deprecate
- Deprecate exceptions unqualified for release candidate.
- Deprecate
EmptyOptionalException
- Deprecate
ExplicitDelimiterRequiredException
, to be refactored toDelimiterException
- Deprecate
GroupNotMatchedException
, to be refactored toGroupException
- Deprecate
IntegerFormatException
- Deprecate
IntegerOverflowException
- Deprecate
InternalCleanRegexException
- Deprecate
InvalidIntegerTypeException
- Deprecate
InvalidReplacementException
, to be refactored to\UnexpectedValueException
- Deprecate
InvalidReturnValueException
- Deprecate
MalformedPcreTemplateException
, to be refactored toSyntaxException
- Deprecate
MaskMalformedPatternException
- Deprecate
NonexistentGroupException
, to be refactored toGroupException
- Deprecate
NoSuchNthElementException
- Deprecate
NoSuchStreamElementException
- Deprecate
PatternMalformedPatternException
- Deprecate
PlaceholderFigureException
- Deprecate
ReplacementExpectationFailedException
- Deprecate
SubjectNotMatchedException
, to be refactored toNoMatchException
- Deprecate
UnevenCutException
- Deprecate
MalformedPatternException
, to be refactored toSyntaxException
- Deprecate
PatternSyntaxException
- Deprecate
CatastrophicBacktrackingException
, to be refactored toBacktrackException
- Deprecate
CompilePregException
, to be refactored toSyntaxException
- Deprecate
JitStackLimitException
, to be refactored toJitException
- Deprecate
PatternStructureException
- Deprecate
PregException
- Deprecate
PregMalformedPatternException
, to be refactored toSyntaxException
- Deprecate
RuntimePregException
, to be refactored toMatchException
- Deprecate
SubjectEncodingException
, to be refactored toUnicodeException
- Deprecate
SuspectedReturnPregException
- Deprecate
- Deprecate
preg::
methods - Deprecate
PcrePattern
, to be refactored toPregPattern
- Deprecate factory methods unqualified for release candidate.
- Deprecate
Pattern.list()
- Deprecate
PatternList
- Deprecate
- Interface of prepared patterns will change in release candidate:
- Deprecate
Pattern.literal()
, to be refactored - Deprecate
Pattern.alternation()
, to be refactored - Deprecate
Pattern.builder()
, to be refactored - Deprecate
Pattern.template()
, to be refactored - Deprecate
PatternTemplate
, to be refactored - Deprecate
TemplateBuilder
, to be refactored
- Deprecate
- Deprecate
Optional
- Deprecate
Pcre
helper - Any class in
Internal/
namespace is subject to change without notice
- Deprecate
- Bug fixes
- Empty alteration previously always matched in-place, now empty alteration never matches.
Pattern::alteration([])
doesn't match anythingPattern::template('@')->alteration([])
doesn't match anythingPattern::builder('@')->alteration([])
doesn't match anything
- Corrected exception when
Pattern::builder()
was used with not enough bound figures.
- Empty alteration previously always matched in-place, now empty alteration never matches.
- Other
- Changed signature of
Pattern::of()
, so$modifiers
argument default value is no longernull
but an empty string. It poses no difference in the behaviour of the method, but the type of the argument is similifed fromnull|string
tostring
in documentation tools.
- Changed signature of
No functional changes.
- Other
- Relaxed composer php version requirement up.
- 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.
- Fixed a bug with improper parsing of character classes starting with
- 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.
- Features
- Updated internal error handling, so T-Regx works in an environment with overriden error handler.
- 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 since8.2
, modifier/n
is fully supported in T-Regx in each PHP version. -
Added full support for
n
modifier inPattern
Pattern::of($pattern, 'n')
is now acceptedPattern::inject($pattern, $figures, 'n')
is now acceptedPattern::template($pattern, 'n')
is now acceptedPattern::builder($pattern, 'n')
is now acceptedPattern::literal($text, 'n')
is now acceptedPattern::alteration($texts, 'n')
is now acceptedPattern::mask($mask, $keywords, 'n')
is now acceptedPattern::list()
now accepts patterns withn
modifier
-
Added full support for
n
modifier inPcrePattern
PcrePattern::of("/$pattern/n", )
is now acceptedPcrePattern::inject("/$pattern/n", $figures)
is now acceptedPcrePattern::template("/$pattern/n")
is now acceptedPcrePattern::builder("/$pattern/n")
is now accepted
-
- Added public modifier constant to
Pattern
.- Added
Pattern::NO_AUTOCAPTURE
- Added
- Since
-
Breaking changes
- Removed
SubjectNotMatchedException.getSubject()
. - Moved
ChainedReplace
toTRegx\CleanRegex\Replace\
- Moved
PatternTemplate
toTRegx\CleanRegex\
- Moved
TemplateBuilder
toTRegx\CleanRegex\
- Updated
MalformedPatternException
messageNull byte in regex
toPattern may not contain null-byte
. - Updated
MalformedPatternException
message...unexpected delimiter '\0'
to...null-byte delimiter
.
- Removed
-
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()
- Fixed the bug in
- Fixed a bug when using prepared patterns with
-
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:
Pattern::of('^foo$', Pattern::CASE_INSENSITIVE . Pattern::MULTILINE)
String literal modifiers continue to work as before
Pattern::of('^foo$', 'im');
- Added
-
-
Other
- Exception
PlaceholderFigureException
now extend correctly implementsPatternException
.
- Exception
- Breaking changes
- Removed
Pattern.forArray().filter()
. UsePattern.filter()
instead. - Removed
Pattern.forArray().filterAssoc()
. - Removed
Pattern.forArray().filterByKeys()
. - Renamed
PatternList.chainedReplace()
toPatternList.replace()
- Removed
- Bug fixes
- Fixed a bug when using
PcrePattern
with delimiter\n
,\v
,\f
,t
didn't throw appropriateMalformedPatternException
- Fixed a bug when using
- Features
- Added
Pattern.filter()
, which filters subjects which match the pattern (exactly like previousPattern.forArray().filter()
). - Added
Pattern.reject()
, which filters subjects which do not match the pattern.
- Added
- Other
- Public exceptions now extend
\RuntimeException
, instead of\Exception
.
- Public exceptions now extend
- Breaking changes
- Revamped
Pattern.replace()
functionality. - Removed
focus()
andsubstitute()
- useful feature, however it proved to be unreliable when used with look-aheads, look-behinds and\K
resets. The functionality is available through a separate"t-regx"
package. - Callback in
replace().callback()
no longer acceptsDetail
orGroup
as a return value.
- Revamped
Migration guide:
- Removed
focus()
- To continue using functionality, use a separate package. - Removed
Group.substitute()
- To continue using functionality, use a separate package. - Removed
replace().by()
- usereplace().callback()
orreplace().withGroup()
- Removed
replace().counting()
- usereplace.count()
- Removed
replace().all()
- omit call toall()
- Updated
replace().callback()
return type - returnDetail.text()
orGroup.text()
as a return value. - Renamed
ReplaceLimit
toReplace
.
- Bug fixes
- Fixed a bug when using internal option setting before newline convention setting could've ended with improper placeholder parsing in comments.
- Features
- Add
PatternList.count()
, which allows to count occurrences of many patterns in a subject.
- Add
- Bug fixes
-
Corrected prepared patterns in malformed patterns.
When malformed pattern is used,
foo(?<bar@cat>door)
(because of unalloyed@
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, soPlaceholderFigureException
is not thrown, butMalformedPatternException
is thrown instead.
-
- Breaking changes
- Renamed
Pattern.compose()
toPattern.list()
- Renamed
CompositePattern
toPatternList
- Renamed
flatMapAssoc()
totoMap()
- Renamed
match().flatMapAssoc()
tomatch().toMap()
- Renamed
search().flatMapAssoc()
tosearch().toMap()
- Renamed
stream().flatMapAssoc()
tostream().toMap()
- Renamed
- Renamed
- Features
-
Corrected
Pattern.mask()
, so it respects the order of keywords, and treats it as the priority of assigning masks.In other words, if there is ambiguity of which keyword should be assigned, when there is no unique solution, the order of keywords is used to determine the mask. Previously it was the length of the keyword, but that caused problems with ambiguous keywords of the same length.
-
Updated
replace().callback()
, so it doesn't unnecessarily causes global search, even when used withonly(int)
orfirst()
.
-
- Breaking changes
- Removed previously deprecated feature of tracking subject modification in replacement
- Removed previously deprecated
ReplaceGroup.modifiedSubject()
- Removed previously deprecated
ReplaceGroup.modifiedOffset()
- Removed previously deprecated
ReplaceGroup.byteModifiedOffset()
- Removed previously deprecated
ReplaceGroup
- Removed previously deprecated
- Removed
ReplaceDetail.limit()
- Removed
ReplaceDetail
- Removed previously deprecated feature of tracking subject modification in replacement
- Breaking changes
- Renamed
MatchPattern
toMatcher
- Revoke certain classes as parts of public library API:
MatchedGroup
is no longer part of public API, useGroup
instead.NotMatchedGroup
is no longer part of public API, useGroup
instead.MatchDetail
is no longer part of public API, useDetail
instead.Intable
is no longer part of public API, returnint
instead.
- Simplified
\TRegx\CleanRegex\Match
namespace- Moved
Detail
to\TRegx\CleanRegex\Match\Detail
- Moved
Group
to\TRegx\CleanRegex\Match\Group
- Moved
Structure
to\TRegx\CleanRegex\Match\Structure
- Moved
Element
to\TRegx\CleanRegex\Match\Element
- Moved
- Renamed
- Deprecation
- Deprecated keeping track of modified subjects
- Deprecated
ReplaceDetail.modifiedSubject()
- Deprecated
ReplaceDetail.modifiedOffset()
- Deprecated
ReplaceDetail.byteModifiedOffset()
- Deprecated
ReplaceGroup.modifiedSubject()
- Deprecated
ReplaceGroup.modifiedOffset()
- Deprecated
ReplaceGroup.byteModifiedOffset()
- Deprecated
- Deprecated replace by map and with group
- Deprecated
replace().by().group()
- Deprecated
replace().by().group().orElseWith()
- Deprecated
replace().by().group().orElseThrow()
- Deprecated
replace().by().group().orElseCalling()
- Deprecated
replace().by().group().orElseIgnore()
- Deprecated
replace().by().group().orElseEmpty()
- Deprecated
replace().by().group().map()
- Deprecated
replace().by().group().mapIfExists()
- Deprecated
replace().by().map()
- Deprecated
replace().by().mapIfExists()
- Deprecated
- Deprecated
ReplaceDetail
. It will be moved toInternal/
namespace in the future release and won't be considered part of public T-Regx API anymore. - Deprecated
ReplaceGroup
. It will be moved toInternal/
namespace in the future release and won't be considered part of public T-Regx API anymore.
- Deprecated keeping track of modified subjects
- Features
-
Prepared patterns now support changing new line conventions with flag
x
(EXTENDED
mode).This code is now valid:
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)
.
-
-
Breaking changes
- Moved
PlaceholderFigureException
fromTRegx\CleanRegex\Internal\Prepared\Figure\
toTRegx\CleanRegex\Exception\
- Moved
-
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()
These constructs are now valid:
Pattern::inject('Find:@?', ['Value']); // matches "Find:Value" and "Find:"
Pattern::inject('Find:@*', ['a']); // matches "Find:", "Find:a", "Find:aa"...
Pattern::inject('Find:@+', ['a']); // matches "Find:a", "Find:aa"...
Pattern::inject('Find:@{3,4}', ['a']); // matches "Find:aaa" and "Find:aaaa"
Pattern::inject('Find:@{0}', ['a']); // matches only "Find:"
- Added support for quantifiers in
-
Added support for empty placeholders in prepared patterns:
Pattern::inject('Find:@?', ['']); // matches only "Find:"
-
Updated backtracking in prepared patterns.
-
Updated backtracking in
Pattern.pattern()
.Pattern::template('@:Bar')->pattern('Foo:Bar|Foo'); // matches "Foo:Bar" or "Foo:Bar:Bar"
-
Updated backtracking in
Pattern.mask()
.$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
overPlaceholderFigureException
.
- Passing invalid arguments which also don't match the number of arguments in
- Breaking changes
-
Removed previously deprecated
Pattern.match().group()
. -
Removed
Pattern.match().asInt()
. Usestream().asInt()
orDetail.toInt()
. -
Pattern.match().first()
no longer acceptscallable
as its argument -
Pattern.match().findFirst()
no longer acceptscallable
as its argument -
Stream.first()
no longer acceptscallable
as its argument -
Stream.findFirst()
no longer acceptscallable
as its argument -
Refactored
Pattern.match()
intomatch()
andsearch()
.Pattern.match()
andPattern.search()
have virtually the same set of methods, with a slight difference. All ofPattern.search()
operate onstring
which is the matched occurrence, and all ofPattern.match()
methods now operate onDetail
.
-
- Features
- Added
Pattern.search()
, which is similar toPattern.match()
, but its methods only operate onstring
. From now on,Pattern.match()
only operates onDetail
.
- Added
- Breaking changes
-
Removed
Pattern.match().tuple()
. -
Removed
Pattern.match().triple()
.These methods were added in earlier versions of T-Regx, when getting two groups was complicated. However now that the library matured enough, there is plenty of ways to get two groups. Additionally,
tuple()
implicitly calls first, but it's not obvious from the look of the method. -
Pattern.match().forEach()
callback no longer receives$index
as its second argument -
Pattern.match().group().forEach()
callback no longer receives$index
as its second argument -
Pattern.match().stream().forEach()
callback no longer receives$key
as its second argument
-
- Features
- Added
Stream.mapEntries()
, similar toStream.map()
, but acceptscallable
with two arguments($key, $value)
, whereasStream.map()
'scallable
accepts($value)
.
- Added
- Breaking changes
- Removed
Detail.usingDuplicateGroup()
. UsingJ
modifier is still allowed, though of little use now. - Previously
Optional.get()
threw exception depending on where the optional came from. NowOptional.get()
always throwsEmptyOptionalException
.
- Removed
- Bug fixes
Pattern.match().groupBy()
now correctly groups by duplicate name withJ
modifier.
- Breaking changes
- Completely refactored
Detail.groups()
andDetail.namedGroups()
- Removed
Detail.groups().count()
- Removed
Detail.groups().texts()
- Removed
Detail.groups().offsets()
- Removed
Detail.groups().byteOffsets()
- Removed
Detail.groups().names()
- Removed
Detail.namedGroups().count()
- Removed
Detail.namedGroups().texts()
- Removed
Detail.namedGroups().offsets()
- Removed
Detail.namedGroups().byteOffsets()
- Removed
Detail.namedGroups().names()
- Removed
- Completely refactored
- Features
-
Now
Detail.groups()
andDetail.namedGroups()
return anarray
ofGroup
.Detail.groups()
returns a sequentialarray
ofGroup
(sequential means indexed0
,1
,2
, etc.)Detail.namedGroups()
returns an associativearray
ofGroup
, where array keys are the group names.
-
- Breaking changes
- Renamed
Detail.hasGroup()
toDetail.groupExists()
. - Renamed
ReplaceDetail.hasGroup()
toReplaceDetail.groupExists()
. - Renamed
Pattern.match().hasGroup()
toPattern.match().groupExists()
.
- Renamed
- Bug fixes
- Calling
Detail.group().asInt()
with invalid integer base on an unmatched group, threwGroupNotMatchedException
. Now it throwsInvalidArgumentException
. - Calling
Detail.group().toInt()
with invalid integer base on an unmatched group, threwGroupNotMatchedException
. Now it throwsInvalidArgumentException
.
- Calling
- Breaking changes
- Removed previously deprecated
NotMatched
. Every functionality ofNotMatched
was also added toPattern.match()
in previous release. This was done to unify the interface of eachOptional
implementation. - Removed previously deprecated
Group.map()
. - Removed
IntStream.asInt()
which returned itself.
- Removed previously deprecated
- Bug fixes
- Fixed a bug when using
usingDuplicateName().group().all()
in certain situations resulted in a list of indexed groups, not duplicately-named groups. - Corrected exception messages of
asInt().limit()
.
- Fixed a bug when using
- Features
- Updated usages of certain methods, like
Detail.groupNames()
and others, so that they don't call full matches when it's not necessary, making it usable even when "regular" usage would throwCatastrophicBacktrackingException
.
- Updated usages of certain methods, like
- Deprecation
- Deprecated inline-groups
Pattern.match().group()
.
- Deprecated inline-groups
- Other
- Previously, just looking up
Detail.group()
caused full match, potentially ending in catastrophic backtracking. Now,group()
doesn't do full match unless it's necessary. Only one call to PREG happens either way, meaning there isn't any performance loss.
- Previously, just looking up
- Breaking changes
-
Removed
Pattern.match().offsets()
. UsePattern.match().map()
. -
Removed
Pattern.match().group().offsets()
. UsePattern.match().group().map()
. -
When using
Pattern.split()
, previously unmatched separators were represented as an emptystring
. Now, they're represented asnull
. -
Previously
Pattern.match().flatMap()
reindexedint
keys, and preservedstring
keys, making it similarly ill-behaved as other PHP functions. From now onflatMap()
treats anyarray
as a sequentialarray
, that is:- It's returned value will be a sequential array (as if
array_values()
was called on it) - Duplicate keys returned from
flatMap(callable)
will not make values no appear in the result. Only the sequence matters.
Method
Pattern.match().flatMapAssoc()
is unchanged, in that case the elements are treated as a dictionary/map - neitherstring
norint
keys are reindexed. - It's returned value will be a sequential array (as if
-
Method
Optional.orThrow()
can no longer be called without an argument. Instead, useOptional.get()
.
-
- Bug fixes
- Fixed a bug when calling
tuple($a,$b)
where$a
was a non-existent group, and$b
was a malformed group, T-Regx threwNonexistentGroupException
, instead ofInvalidArgumentException
. Now it throwsInvalidArgumentException
. - Fixed a bug when calling
triple($a,$a,$b)
where$a
was a non-existent group, and$b
was a malformed group, T-Regx threwNonexistentGroupException
, instead ofInvalidArgumentException
. Now it throwsInvalidArgumentException
. - Updated misleading message of
GroupNotMatchedException
thrown when usinggroupByCallback()
.
- Fixed a bug when calling
- Features
- Added
Pattern.splitStart()
, which works similarly toPattern.split()
but accepts a non-negativemaxSplits
argument, which can be used to limit splits from the start of the subject. - Added
Pattern.splitEnd()
, which works similarly toPattern.split()
but accepts a non-negativemaxSplits
argument, which can be used to limit splits from the end of the subject. - Added method
Optional.get()
, which returns the optional value or throws an exception (similarly to previousOptional.orThrow(null)
). - Added methods to
Pattern.match()
:- Added
Pattern.match().subject()
, same asDetail.subject()
andNotMatched.subject()
. - Added
Pattern.match().hasGroup()
, same asDetail.hasGroup()
andNotMatched.hasGroup()
. - Added
Pattern.match().groupNames()
, same asDetail.groupNames()
andNotMatched.groupNames()
. - Added
Pattern.match().groupsCount()
, same asDetail.groupsCount()
andNotMatched.groupNames()
.
- Added
- Added
- Deprecation
- Deprecated
NotMatched
passed asfindFirst().orElse()
argument.
- Deprecated
- Breaking changes
match().groupBy(string|int)
now returnsarray
ofDetail
, previously returnedarray
ofstring
.- Removed
match().groupBy().all()
. Usematch.groupBy()
- Removed
match().groupBy().offsets()
. Usestream().groupByCallback()
- Removed
match().groupBy().byteOffsets()
. Usestream().groupByCallback()
- Removed
match().groupBy().map()
. Usestream().groupByCallback().map()
- Removed
match().groupBy().flatMap()
. Usestream().groupByCallback().flatMap()
- Removed
match().groupBy().flatMapAssoc()
. Usestream().groupByCallback().flatMapAssoc()
- Previously
groupBy()
simply ignored unmatched groups. NowGroupNotMatchedException
is thrown. To control unmatched elements useGroup.matched()
,stream().filter()
or other elements of T-Regx API.
- Breaking changes
-
Detail.group()
no longer implementsOptional
-
Detail.usingDuplicateName().group()
no longer implementsOptional
-
Removed previously deprecated
Detail.group().orReturn()
. Useor()
instead. -
Removed previously deprecated
Detail.group().orElse()
-
Removed previously deprecated
Detail.group().orThrow()
-
Removed previously deprecated
ReplaceDetail.group().orReturn()
. Useor()
instead. -
Removed previously deprecated
ReplaceDetail.group().orElse()
-
Removed previously deprecated
ReplaceDetail.group().orThrow()
-
Updated how
Stream.distinct()
removes elements:- Now
'1'
andtrue
are no longer considered equal - Now
''
andfalse
are no longer considered equal - Now
0
andfalse
are no longer considered equal - Now
1
andtrue
are no longer considered equal - Now
0
and'0'
are no longer considered equal - Now
null
andfalse
are no longer considered equal
For all intents and purposes, now
Stream.distinct()
works as-if it used strict-comparison===
. - Now
-
Stream.filter()
no longer reindexes stream elements. To reindex them, chain the stream withvalues()
.match().filter()
still returns a sequential array with reindexed values. -
Removed
Stream.only()
. UseStream.limit().all()
instead. -
Removed
IntStream.only()
. UseIntStream.limit().all()
instead. -
Renamed
Group.textLength()
toGroup.length()
. -
Renamed
Group.textByteLength()
toGroup.byteLength()
. -
Renamed
Detail.textLength()
toDetail.length()
. -
Renamed
Detail.textByteLength()
toDetail.byteLength()
.
-
- Bug fixes
- Fixed a bug when using
Stream.values().keys().first()
didn't always reindex to0
. - Fixed a bug when using regular groups in
stream()->asInt()
was allowed, butusingDuplicateName()
groups weren't. Now both kinds of groups are correctly passed intostream()->asInt()
. - Fixed a bug when using regular groups in
groupByCallback()
was allowed, butusingDuplicateName()
groups weren't. Now both kinds of groups are correctly passed intogroupByCallback()
.
- Fixed a bug when using
- Features
- Added
Detail.group().or()
which behaves similarly toorReturn()
but only accepts a non-nullablestring
. - 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 end
- Added
- Other
- Previously, using
Stream.keys().first()
return0
for sequential arrays, and T-Regx didn't evaluate previous chains, such asmap()
orflatMap()
. As of this release, they will be called for completeness, even though their results won't be used.
- Previously, using
- Breaking changes
- Removed previously deprecated
Detail.limit()
.ReplaceDetail.limit()
remains unchanged. - Integer methods (
toInt()
,isInt()
,asInt()
) accept$base
as their optional argument, which defaults to10
.- Previously passing
null
as argument to integer methods was allowed, which also defaulted to10
. Currently, the argument can either be of typeint
or omitted, butnull
is no longer allowed as$base
.
- Previously passing
- Refactored
Pattern::pcre()->of()
toPcrePattern::of()
- Refactored
Pattern::pcre()->inject()
toPcrePattern::inject()
- Refactored
Pattern::pcre()->builder()
toPcrePattern::builder()
- Refactored
Pattern::pcre()->template()
toPcrePattern::template()
- Removed
Pattern::pcre()
- Removed previously deprecated
- Deprecation
- Deprecated
Detail.group().orReturn()
- Deprecated
Detail.group().orElse()
- Deprecated
Detail.group().orThrow()
- Deprecated
Detail.group().map()
- Deprecated
ReplaceDetail.group().orReturn()
- Deprecated
ReplaceDetail.group().orElse()
- Deprecated
ReplaceDetail.group().orThrow()
- Deprecated
ReplaceDetail.group().map()
- Deprecated
- Bug fixes
- Normally, pattern errors take precedence over subject errors. That is,
NonexistentGroupException
should be thrown beforeCatastrophicBacktrackingException
.- Fixed a bug when replacing a pattern prone to catastrophic backtracking and unmatched group
threw
CatastrophicBacktrackingException
. Now it throwsNonexistentGroupException
.
- Fixed a bug when replacing a pattern prone to catastrophic backtracking and unmatched group
threw
- Normally, pattern errors take precedence over subject errors. That is,
- Breaking changes
- Previously deprecated
Optional.orThrow()
accepted exception class name asstring
. Currently,orThrow()
accepts an instance of\Throwable
. - Removed
ClassExpectedException
, which was thrown when an invalid class name was passed toorThrow()
. - Removed
NoSuitableConstructorException
, which was thrown when an invalid class was passed toorThrow()
. - Previously T-Regx used whatever encoding was set for
mb_internal_encoding()
forDetail.offset()
/tail()
/textLength()
. Now, T-Regx always uses UTF-8 regardless ofmb_internal_encoding()
. For byte manipulation in encoding other than UTF-8 usebyteOffset()
/byteTail()
/byteTextLength()
. - Removed previously deprecated
Detail.setUserData()
/getUserData()
- Previously deprecated
- Bug fixes
- Fixed a bug when using
match()->groupByCallback()
didn't throwInvalidReturnValueException
for invalid group value
- Fixed a bug when using
- Deprecation
- Deprecated
Detail.limit()
- Deprecated
- Breaking changes
- Changed the namespace of
LazyDetail
toTRegx\CleanRegex\Replace\By\LazyDetail
.
- Changed the namespace of
- Bug fixes
-
Fixed a bug when
groups()->texts()
withLazyDetail
returned''
for an unmatched group on PHP7.1.LazyDetail
is the implementation ofDetail
used withreplace()->orElseCalling()
. -
Fixed a bug when using
replace()->by()->group()
with a nonexistent group, didn't throwNonexistentGroupException
for unmatched subject.
-
- Deprecation
- User-data in
Detail
will be removed in future release- Deprecated
Detail.setUserData()
- Deprecated
Detail.getUserData()
- Deprecated
- User-data in
- Other
- Updated
replace()->by()->group()
, so that certain scenarios which would normally throwCatastrophicBacktrackingException
, are handled in such a way that no catastrophic backtracking happens.
- Updated
- Breaking changes
- Renamed
GroupLimit
toGroupMatch
. - Previously
nth()
/findNth()
threw different exceptions when the subject wasn't matched and when the item was missing. Now they always throwNoSuchNthElementException
(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()
throwsNoSuchNthElementException
, instead ofSubjectNotMatchedException
match()->asInt()->nth()
throwsNoSuchNthElementException
, instead ofNoSuchStreamElementException
match()->stream()->nth()
throwsNoSuchNthElementException
, instead ofNoSuchStreamElementException
match()->group()->nth()
throwsNoSuchNthElementException
, instead ofSubjectNotMatchedException
match()->group()->asInt()->nth()
throwsNoSuchNthElementException
, instead ofNoSuchStreamElementException
match()->group()->stream()->nth()
throwsNoSuchNthElementException
, instead ofNoSuchStreamElementException
- Renamed
- Bug fixes
match()->offsets()
andmatch()->group()->offsets()
now return offsets as characters. Previously they returned them as bytes.
- Features
- Added
SubjectNotMatchedException.getSubject()
- Added
- Performance
- Improved performance of certain validations, by not parsing PHP errors, when it's not necessary.
- Features
- Added
pattern()->match()->asInt()->reduce()
- Added
- Compatibility fixed
- Removed usage of
str_contains()
andstr_starts_with()
from previous version, to maintain support for PHP 7.1.
- Removed usage of
- Bug fixes
- Fixed a bug when using
match()->groupBy()
for a nonexistent group didn't throwNonexistentGroupException
.
- Fixed a bug when using
- Features
- Added
Pattern.cut()
which splits string into exactly two parts.
- Added
- 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()
.
- Deprecated
- Bug fixes
- 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 inPattern::inject()
/Pattern::template()
. - Corrected a bug when using modifier reset
(?^)
didn't properly unset extended pattern, leading to improper placeholder@
parsing inside comments.
- Corrected a bug in prepared patterns, where closing group after in-pattern (
- Other
- Previously T-Regx removed duplicate flags before passing them to PCRE. Now flags are passed without modification.
Footnote (15.03.2022):
- Accidental compatibility breaks:
- We mistakenly used
str_contains()
andstr_starts_with()
. Our tests didn't find it, because we also usedphp-coveralls/php-coveralls
which introduced polyfill for PHP8 methods. It's fixed in 0.20.2.
- We mistakenly used
- Breaking changes
- Removed
pattern()->match()->remaining()
, as it was a failed idea.
- Removed
- Bug fixes
- Fixed a bug when
stream()->map()->asInt()
ignored integer-base, assumed base 10.
- Fixed a bug when
- Features
- Added
pattern()->match()->reduce()
- Added
- Features
- Added
$flags
parameter topreg::replace_callback()
which was added in PHP 7.4.
- Added
- 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.
- Fixed a bug when pattern
- Breaking changes
- Rename
BaseDetail
toStructure
- Rename
- Features
- Add second argument
Structure
as second parameter toreplace()->counting()
- Add second argument
- Features
replace()->by()->map()
allows for replacingint
and numeric strings ("12"
)
- Breaking changes
- Removed
replace()->otherwise()
. Usecounting()
instead. - Removed
replace()->otherwiseReturning()
. Usecounting()
instead. - Removed
replace()->otherwiseThrowing()
. Usecounting()
oratLeast()
instead.
- Removed
- Other
- T-Regx follows Semantic Versioning 2
- Bug fixes
- Fixed a bug when returning non-string value from
orElseCalling()
didn't throwInvalidReturnValueException
- Fixed a bug when returning non-string value from
- Breaking changes
- Renamed
FluentMatchPattern
toStream
, similar to Java 8 streams - Renamed
fluent()
tostream()
, similar to Java 8 streams - Renamed
NoSuchElementFluentException
toNoSuchStreamElementException
- Renamed
- Features
- Add
IntStream.stream()
- Add
- Breaking changes
-
Removed
FluentMatchPatternException
. In case ofasInt()
,InvalidIntegerTypeException
is thrown instead. -
Methods
asInt()
andoffsets()
returnIntStream
instead ofFluentMatchPattern
. -
Updated the rules when exceptions are thrown from
asInt()
,offsets()
andfluent()
:- Exceptions thrown from
IntStream
:pattern()->match()->asInt()
throwsSubjectNotMatchedException
pattern()->match()->offsets()
throwsSubjectNotMatchedException
pattern()->match()->group()->asInt()
throwsSubjectNotMatchedException
orGroupNotMatchedException
pattern()->match()->group()->offsets()
throwsSubjectNotMatchedException
orGroupNotMatchedException
- Exception thrown from
FluentMatchPattern
:pattern()->match()->fluent()
throwsNoSuchElementFluentException
pattern()->match()->asInt()->fluent()
throwsNoSuchElementFluentException
pattern()->match()->offsets()->fluent()
throwsNoSuchElementFluentException
Basically,
MatchPattern
andIntStream
throw match-related exceptions (SubjectNotMatchedException
orGroupNotMatchedException
), whereasFluentMatchPattern
throws fluent-related exception:NoSuchElementFluentException
. - Exceptions thrown from
-
Updated exception messages from
asInt()
,offsets()
andfluent()
. -
MatchPatternInterface
is no longer part of T-Regx public API.
-
- Breaking changes
- Renamed
Pattern::template()
toPattern::builder()
- Renamed
- Features
- Added
Pattern::template()
, which works similarly toPattern::builder()
but allows only one chain
- Added
- Bug fixes
- Fixed a bug when calling
filter()->first()
called predicate for more than the first item.
- Fixed a bug when calling
- Deprecation
- Deprecated
Optional.orThrow()
. CurrentlyorThrow()
accepts the exception class name. In the future it will accept a real\Throwable
instance. To preserve current behaviour oforThrow()
, useorElse()
.
- Deprecated
- Breaking changes
- Renamed
Pattern::compose()->allMatch()
totestAll()
- Renamed
Pattern::compose()->anyMatches()
totestAny()
- Renamed
Pattern::compose()->chainedRemove()
toprune()
- Renamed
- Features
- Added
Pattern::compose()->failAny()
, returningtrue
if any of the patterns didn't match the subject - Added
Pattern::compose()->failAll()
, returningtrue
if all the patterns didn't match the subject
- Added
- 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 regarding 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 inX
tended mode, all T-Regx entry points correctly recognize it as valid
- PHP reports
- Fixed a bug, where using
- Features
- Added
Optional.map()
, which resembles Java 8 optionals. pattern()->match()->asInt()->findFirst()->orElse()
receiveNotMatched
argumentpattern()->match()->asInt()->findNth()->orElse()
receiveNotMatched
argumentpattern()->match()->offsets()->findFirst()->orElse()
receiveNotMatched
argumentpattern()->match()->offsets()->findNth()->orElse()
receiveNotMatched
argument
- Added
- Breaking changes
Pattern::inject()
no longer supports alteration. UsePattern::template()->alteration()
.
- Features
- Added
pattern()->match()->forEach()
consumer accepts index as a second argument
- Added
- Bug fixes
- Fixed a bug, where using
match()->filter()
didn't throwInvalidReturnValueException
. - Fixed a bug, where using
group()->filter()
didn't throwInvalidReturnValueException
. - Fixed a bug, where
Pattern::template()->mask()
keywords weren't taken into account, when choosing a delimiter
- Fixed a bug, where using
- Features
- Added
Pattern::template()->pattern()
- Added
- Others
- Updated
ExplicitDelimiterRequiredException
message forPattern::of()
- Updated
ExplicitDelimiterRequiredException
message forPattern::mask()
- Updated
ExplicitDelimiterRequiredException
message forPattern::template()
- Updated
- Breaking changes
- Refactored
Pattern::pcre()
toPattern::pcre()->of()
- Refactored
Pattern::builder()->pcre()->inject()
toPattern::pcre()->inject()
- Refactored
Pattern::builder()->pcre()->template()
toPattern::pcre()->template()
- Removed
Pattern::builder()
. - Moved
ReplaceDetail
toTRegx\CleanRegex\Replace\Detail
namespace - Moved
ReplaceGroup
toTRegx\CleanRegex\Replace\Detail\Group
namespace
- Refactored
- Features
- Added
Pattern::alteration()
which allows buildingPattern
with just an alteration group.- For example
Pattern::alteration(['foo', 'bar'])
is/(?:foo|bar)/
- For example
- Added
Pattern::template()->alteration()
- Added
- Bug fixes
- Fixed a bug, where passing
false
as an alteration value didn't throw\InvalidArgumentException
.
- Fixed a bug, where passing
- Features
- Every method
toInt()
/isInt()
receives a$base
optional argument, which defaults to10
:Detail.toInt()
,Detail.isInt()
,Group.toInt()
,Group.isInt()
,ReplaceDetail.toInt()
,ReplaceDetail.isInt()
,pattern()->match()->asInt()
pattern()->match()->group()->asInt()
- Every method
- Other
- We added continuous integration runs for PHP on 32-bit architecture, to test 32-bit integers with
toInt()
.
- We added continuous integration runs for PHP on 32-bit architecture, to test 32-bit integers with
- Bug fixes
- Fixed w bug where using
Detail.usingDuplicateName()
didn't throwNonexistentGroupException
.
- Fixed w bug where using
- Bug fixes
- Fixed a bug when using
match()->asInt()->keys()->first()
for malformed integers would not throwNumberFormatException
. - Fixed a bug when using
fluent()->keys()->keys()
(doublekeys()
) would silence T-Regx exceptions.
- Fixed a bug when using
- Breaking changes
- Previously
remaining()
andfilter()
would leave a resulting array with keys that aren't exactly sequential, giving the impression the iterated collection is not a list. Now it's fixed, so the resulting array is indexed.
- Previously
- Bug fixes
- Fixed a bug when using
match()->fluent()->first()
exposed a false-negative inhasGroup()
from PHP. - Fixed a bug when using
match()->group()->fluent()->first()
exposed a false-negative inhasGroup()
from PHP.
- Fixed a bug when using
- Other
- Internal implementation revamp
- Breaking changes
pattern()->forArray()
is now the same as previouspattern()->forArray()->strict()
.- Removed
pattern()->forArray()->strict()
. - Removed
Pattern::quote()
. Usepreg::quote()
, which behaves in exactly the same way. - Move
Pattern::unquote()
topreg::unquote()
, which behaves in exactly the same way. - Removed
pattern()->remove()->all()
. Usepattern()->prune()
instead. - Removed
pattern()->match()->asArray()
. UseDetail.groups()
orDetail.namedGroups()
instead.
- Other
- Using TDD in T-Regx, it wasn't hard to reach 100% coverage quite easily in T-Regx. In order to make the tests even better, we decided that the integration tests won't report any coverage, since it doesn't provide any more information now (it's always 100%). That doesn't mean all the cases are tested tough, so we decided to disable the coverage reports on the Integration tests. Now, the coverage badge will drop, but that doesn't mean we remove any tests. We just mark them as "non-reportable", so that we can use the coverage to actually find more untested cases.
- 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 notably
[@]
,\Q@\E
,\@
,\c@
and others, like comment groups and comments in extended mode.
- 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 notably
- 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, ifx
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.
- Previously,
- Mask placeholders are no longer represented as
&
in templates, use@
. - Refactored
Pattern::template()->builder()
. UsePattern::template()
now. - Removed
Pattern::bind()
. UsePattern::inject()
orPattern::template()->literal()
. - Removed
Pattern::prepare()
. UsePattern::inject()
. - Removed
Pattern::pcre()->bind()
. - Removed
Pattern::pcre()->prepare()
. - Removed
Pattern::template()->bind()
.
- Prepared patterns now use internal regular expression parser, to determine what is a placeholder and what isn't:
- Bug fixes
- Correct type-error in
ValidPattern.isValid()
on PHP 8.1.
- Correct type-error in
- Features
- Added
Detail.usingDuplicateName().get()
#101 - Added
Detail.usingDuplicateName().matched()
#101 - Method
Pattern:template()->literal(string)
now acceptsstring
argument, allowing for inserting arbitrary strings into the pattern. - Added
Pattern::builder()
, which works similarly to howPatternBuilder::builder()
worked. - Added
Pattern::literal()
which creates an instance of a pattern with which matches an arbitrary string exactly, even whenx
(EXTENDED
) flag is used. To add in-pattern structures, like^
or$
, usePattern::template()->literal()
. - Added
Pattern::template()->literal()
, which is a shorthand forPattern::template()->builder()->literal()->build()
. - Added
Pattern::template()->mask()
, which is a shorthand forPattern::template()->builder()->mask()->build()
. - Casting
PatternInterface
tostring
results in a delimited pattern - Add
Pcre
version helper
- Added
- Breaking changes
match()->getIterator()
no longer preserves the keys of values (likeall()
)match()->group()->getIterator()
no longer preserves the keys of values (likeall()
)- Renamed
Pattern::format()
toPattern::mask()
- Renamed
Pattern::builder()->format()
toPattern::builder()->mask()
- Renamed
Pattern::template()->format()
toPattern::template()->mask()
- Refactored
Pattern::template()->formatting()
toPattern::template()->builder()->mask()
- Method
literal()
now requires argument'&'
, to escape&
in-pattern token - Removed
PatternBuilder::builder()
. UsePattern::builder()
- Removed
PatternBuilder::compose()
. UsePattern::compose()
- Renamed
FormatMalformedPatternException
toMaskMalformedPatternException
- Removed interface
PatternInterface
. Now classPattern
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.
- Breaking changes
- Rename
DetailGroup
toGroup
- Rename
ReplaceDetailGroup
toReplaceGroup
- Rename
BaseDetailGroup
toCapturingGroup
- Rename
- Features
- Calling
pattern()->replace()
withoutall()
/first()
/only()
, implicitly assumesall()
- Calling
- Bug fixes
- Group name
"group\n"
used to be considered valid, now it's correctly being treated as invalid.
- Group name
- 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
- Breaking changes
-
Chainable
pattern()->match()->filter()
is renamed toremaining()
.pattern()->match()->fluent()->filter()
is not being renamed. -
After filtering
MatchPattern
withremaining()
, consecutiveDetail.index()
will no longer be reindexed, they will preserve theindex()
they had had beforeremaining()
. -
match()->fluent()->filter()
no longer reindexes values. To reindex, usevalues()
.
-
- Bug fixes
- Fixed a bug where
fluent()->flatMap()->first()
would return thearray
, instead of the first element
- Fixed a bug where
- Features
- Add
pattern()->match()->filter()
which returns only matches allowed by the predicate. - Add
pattern()->match()->group()->asInt()
- Add
- Other
pattern()->match()->fluent()->filter()->first()
first callspreg_match()
, and if that result doesn't match the predicate, then it callspreg_match_all()
.
- Breaking changes
-
Previously deprecated
Match
andReplaceMatch
are now being removed, because of PHP8 keywordmatch
.Use
Detail
andReplaceDetail
instead.
-
- Other
- T-Regx version 0.10 supports PHP 8.
-
Breaking changes
- Rename
DetailGroup.replace()
toDetailGroup.substitute()
- Rename
match().groupBy().texts()
tomatch().groupBy().all()
ReplaceDetail.modifiedOffset()
returned values as bytes, now returns them as charactersReplaceDetailGroup.modifiedOffset()
returned values as bytes, now returns them as characters- Move
MalformedPatternException
to namespace\TRegx\Exception
- Move
RegexException
to namespace\TRegx\Exception
MalformedPatternException
was a class extendingCompilePregException
. Now,MalformedPatternException
extends onlyRegexException
. New class,PregMalformedPatternException
is being thrown everywhereMalformedPatternException
used to be thrown. Don't refactor yourcatch (MalformedPatternException $e)
, since that's still the recommended handling. (but sayget_class()
would returnPregMalformedPatternException
). Complete exception structure is described in "Exceptions".- Rename exceptions
- Rename
Utf8OffsetPregException
toUnicodeOffsetException
- Rename
SubjectEncodingPregException
toSubjectEncodingException
- Rename
CatastrophicBacktrackingPregException
toCatastrophicBacktrackingException
- Rename
RecursionLimitPregException
toRecursionException
- Rename
JitStackLimitPregException
toJitStackLimitException
- Rename
- Rename
-
Bug fixes
-
Fix a security bug in
Pattern::bind()
-
Using pattern with a trailing backslash (e.g.
"(hello)\\"
) would throwMalformedPatternException
with a really weird message, exposing the implementation details. Now the message isPattern may not end with a trailing backslash
. -
Adapt
focus()->withReferences()
so it works exactly aspreg_replace()
.Previously, using a nonexistent or unmatched group with
focus()->withReferences()
would throw an exception. But of course,preg_replace()
references$1
and\1
simply are ignored by PCRE, being replaced by an empty string. So, as of this version bothwithReferences()
andfocus()->withReferences()
ignore the unmatched or nonexistent group as well. -
Fix an error where optionals didn't work properly for
match()->offsets()->fluent()
-
Fix an error where
ReplaceDetail
would return malformedmodifiedSubject()
for utf-8 replacements
-
-
Features
-
Add
ReplaceDetail.byteModifiedOffset()
which returns values as bytes -
Add
ReplaceDetailGroup.byteModifiedOffset()
which returns values as bytes -
Add pattern formats and pattern templates, a new way of creating pseudo-patterns for user supplied data:
- Add
Pattern::format()
#79 - Add
Pattern::template()
#79
- Add
-
Add
Detail.textByteLength()
#88 -
Add
DetailGroup.textByteLength()
#88 -
Add
match()->flatMapAssoc()
#88 -
Add
match()->group()->flatMapAssoc()
#88 -
Add
match()->fluent()->flatMapAssoc()
#88 -
Add
match()->groupBy()->flatMapAssoc()
#88Otherwise identical to
flatMap()
, but sinceflatMapAssoc()
doesn't usearray_merge()
, theint
keys won't be reindexed - returning an integer key from aflatMapAssoc()
. If a given key was already returned previously, the later value will be preserved. It's useful for associative arrays withint
keys. For sequential arrays (or arrays withstring
keys), feel free to useflatMap()
. -
Add
match()->groupByCallback()
(previously onlymatch()->fluent()->groupByCallback()
andmatch()->groupBy()
) #80 -
Add
match()->nth()
(previously onlymatch()->fluent()->nth()
) #80 -
Add
replace()->counting()
, invoking a callback with the number of replacements performed #90 -
Add
replace()->exactly()
, validating that exactly one/only replacements were performed #90 -
Add
replace()->atLeast()
, validating that at least one/only replacements were performed #90 -
Add
replace()->atMost()
, validating that at most one/only replacements were performed #90 -
Add
pattern()->prune()
which removes every occurrence of a pattern from subject (identical toremove()->all()
)
-
-
Other:
- Replace any usage of
\d
to[0-9]
in the library, since it depends on PHP locale. - Added interface
PatternStructureException
which can be used to catch exceptions for errors solely in pattern structure (recursion, backtracking, jit limit).
- Replace any usage of
-
Breaking changes
- None
-
Deprecation
-
Deprecate
ReplaceMatch
, useReplaceDetail
instead. -
Deprecate
MatchGroup
, useDetailGroup
instead. -
Deprecate
ReplaceMatchGroup
, useReplaceDetailGroup
instead.In preparation for PHP 8, in which
match
is a new keyword, we deprecateMatch
andReplaceMatch
.Match
will become an invalid class name in PHP 8.Classes
Match
,ReplaceMatch
,MatchGroup
andReplaceMatchGroup
will remain in T-Regx (as deprecated) as long as T-Regx doesn't support PHP 8.
-
Features
-
Add
NotReplacedException.getSubject()
-
Add
DetailGroup.subject()
-
Add
ReplaceDetailGroup.subject()
-
Add
pattern()->replace()->focus(group)
#82It allows the replacement mechanism to focus on a single group, so only the focused capturing group will change; the rest of the whole match will be left as it was.
-
Added proper handling of
/J
flag #84Previously, duplicate patterns added a form of unpredictability - the structure of the group (order, index, name) depended on the group appearance in the pattern, which is fine. However, its value (text, offset) depended on which group was matched (that's what we call strategy 2). That's the consequence of php storing only one named group in the result, since PHP arrays can't hold duplicate keys.
That's another gotcha trap set by PHP, and we need a reasonable mechanism in T-Regx to handle it.
Since now, every method (inline groups, group in
Match
, etc.) predictably depends on the order of the group in the pattern (that's what we call strategy 1), even the value (text, offset), which previously were kind of random. -
Added
Match.usingDuplicateName()
method, which allows the user to use the less predictable behaviour (which was the default, previously).For safety, groups returned from
usingDuplicateName()
don't haveindex()
method, since it allows strategy 2, and strategy 2 indexes of groups are sometimes unpredictable. Group returned there extends a different interface, notDetailGroup
as usual, butDuplicateNamedGroup
- that's an otherwise identical interface, except it doesn't haveindex()
method. Of course, regulargroup(int|string)
groups still haveindex()
method, since they use strategy 1 now.Match.group('group')
previously would return strategy 2, now returns strategy 1.Match.usingDuplicateName().group('group')
returns group by strategy 2 (previously default)
There is currently no way to use strategy 2 for inline groups or aggregate group methods, only for
Match
/Detail
details.
-
-
Other
- Updated some exceptions' messages format; most notably, indexed groups as formatted as
#2
, and named groups as'group'
.
- Updated some exceptions' messages format; most notably, indexed groups as formatted as
-
SafeRegex
-
After calling
preg_match()
with overflowing offset,preg_last_error()
would returnPREG_INTERNAL_ERROR
, which T-Regx would handle, throwingRuntimePregException
with proper message. Negative offsets would be ignored.Since now, T-Regx throws
\InvalidArgumentException
in both cases.
-
- Bug fixes
-
Breaking changes
- Added
null
-safety topattern()->replace()
:- Returning
null
fromreplace()->callback()
throwsInvalidReturnValueException
. - Returning
null
fromreplace()->otherwise()
throwsInvalidReturnValueException
. - Returning
null
fromreplace()->by()->group()->orElse()
throwsInvalidReturnValueException
.
- Returning
- Renamed
pattern()->replace()->by()->group()
methods:- Renamed
orThrow(string)
toorElseThrow(string)
. - Renamed
orIgnore()
toorElseIgnore()
. - Renamed
orEmpty()
toorElseEmpty()
. - Renamed
orReturn(string)
toorElseWith(string)
. - Renamed
orElse(callable)
toorElseCalling(callable)
.
- Renamed
- Renamed and added
pattern()->replace()->by()->group()->map()
methods:- Renamed
orReturn(string)
toorElseWith(string)
. - Renamed
orElse(callable)
toorElseCalling(callable)
. - Renamed
orThrow(string)
toorElseThrow(string)
. - Added
orElseIgnore()
. - Added
orElseEmpty()
.
- Renamed
- Added
-
Features
- Prepared patterns:
- Restored
Pattern::prepare()
, but without alteration. #78 - Restored
PatternBuilder::prepare()
, but without alteration. #78
- Restored
- Match tail (as
offset()
, but from the end-side): #83- Add
Match.tail()
. - Add
Match.byteTail()
. - Add
MatchGroup.tail()
. - Add
MatchGroup.byteTail()
. - Add
ReplaceMatchGroup.tail()
. - Add
ReplaceMatchGroup.byteTail()
.
- Add
- Added method
getPregPattern()
to exceptions: #85PregException
CompilePregException
MalformedPatternException
RuntimePregException
SubjectEncodingPregException
Utf8OffsetPregException
CatastrophicBacktrackingPregException
RecursionLimitPregException
JitStackLimitPregException
InvalidReturnValueException
- Prepared patterns:
-
Fixed inconsistencies
- Duplicated pattern exception message changes offset after PHP 7.3. Since now, the messages will be identical on every PHP version.
- Breaking changes
- Renamed
BacktrackLimitPregException
toCatastrophicBacktrackingPregException
. - Removed
Pattern::prepare()
. - Removed
PatternBuilder::prepare()
. - Renamed
throwingOtherwise()
tootherwiseThrowing()
. - Renamed
returningOtherwise()
tootherwiseReturning()
.
- Renamed
- Features
- Add
pattern()->match()->tuple()
method. #76 - Add
pattern()->match()->triple()
method. #76
- Add
- Breaking changes
- Renamed
pattern()->delimiter()
topattern()->delimited()
- Renamed
- 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 (likeby()->map()
), and then passes it through callback, before replacing (likecallback()
).
- Add
- Enhancements
- Prepared patterns correctly handle whitespace with
PCRE_EXTENDED
mode. #40
- Prepared patterns correctly handle whitespace with
- SafeRegex
preg::quote()
throwsInvalidArgumentException
when it's called with a delimiter that's not a single character.- Handled PHP Bug #77827, when
\r
was passed at then end of a pattern topreg_match()
/preg_match_all()
.
- Bug fixes
- Fixed a bug in Prepared patterns (PCRE mode), when using a malformed pattern caused
TypeError
, instead ofMalformedPatternException
.
- Fixed a bug in Prepared patterns (PCRE mode), when using a malformed pattern caused
- Features
- You can now use
foreach
onmatch()
, instead offorEach()
:and alsoforeach (Pattern::of('\d+')->match('127.0.0.1') as $match) {}
orforeach (Pattern::of('\d+')->match('127.0.0.1')->asInt() as $digit) {}
foreach (Pattern::of('\d+')->match('127.0.0.1')->all() as $text) {}
- Added
Match.get(string|int)
, which is a shorthand forMatch.group(string|int).text()
. - Restored
pattern()->match()->test()
/fails()
that were removed in version 0.9.2.
- You can now use
- Breaking changes
pattern()->replace()->orElse/Throw/Return->with()
are renamed tootherwise()
/throwingOtherwise()
/returningOtherwise()
.
- Features
- Added
pattern()->match()->asArray()->*
which returns results as an array (as if it was returned bypreg_match()
, but fixed). More below.
- Added
- Bug fixes
- Fixed a bug when
findFirst()
sometimes calledpreg_match_all()
, despite previous change.
- Fixed a bug when
When using preg_match()
or preg_match_all()
with PREG_SET_ORDER
, the last groups that are unmatched or
matched an empty string are removed by PHP! Missing group, unmatched group and group that matched ""
are
indistinguishable. Basically, PHP trims any false
-y group.
T-Regx fixes it by filling the results:
null
always means a group is present, but unmatched""
means a matched group, that matched an empty string
- 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.
- To re-index keys, use
- Rename
NoFirstElementFluentException
toNoSuchElementFluentException
- Enhancements 🔥
- Every
match()->...()->first()
method callspreg_match()
, instead ofpreg_match_all()
. More below.
- Every
- Features
- Added
pattern()->match()->fluent()->nth(int)
used to get an element based on an ordinal number. - Added
pattern()->match()->asInt()
. More below.
- Added
Previously preg_match()
was called only by:
Any other match()
method (e.g. map()
, forEach()
, etc.) used preg_match_all()
. From now on, where
possible, preg_match()
is also used for:
fluent()->first()
asInt()->first()
/asInt()->fluent()->first()
group()->first()
offsets()->first()
group()->offsets()->first()
- Any method after
fluent()
, for examplefluent()->map()->first()
The same applies to the methods above ending with findFirst()
.
The change was made because of two reasons:
- Performance (matching only the first occurrence is faster than all of them)
- There are cases where the 2nd (or 3rd, n-th) occurrence would have thrown an error (e.g. catastrophic backtracking).
Now, such string can be worked with, by calling
preg_match()
and returning right after first match.
The only exception to this rule is filter()->first()
, which still calls preg_match_all()
.
- New method
asInt()
can be chained with anymatch()
method:match()->asInt()->all(): int[];
match()->asInt()->only(int $limit): int[];
match()->asInt()->first(callable $consumer = null): int;
match()->asInt()->forEach(callable $consumer): void;
match()->asInt()->findFirst(callable $consumer): Optional<int>;
match()->asInt()->count(): int;
though it doesn't change anythingmatch()->asInt()->iterator(): \Iterator<int>;
match()->asInt()->map(callable $mapper): int[];
match()->asInt()->flatMap(callable $mapper);
match()->asInt()->distinct(): int[];
match()->asInt()->filter(callable $predicate): int[];
- Callbacks passed to
first()
/map()
/flatMap()
etc. receiveint
. asInt()->fluent()
is slightly better thanfluent()->asInt()
:fluent()->asInt()
createsMatch
details for each occurrence, which are then cast toint
.asInt()->fluent()
simply returns matches asint
.
- Breaking changes
-
Removed:
pattern()->match()->fluent()->iterate()
pattern()->match()->group()->iterate()
pattern()->match()->group()->fluent()->iterate()
as
iterate()
was only needed as a substitute forforEach()
, pre PHP 7, where methods couldn't be named with keywords. -
Renamed:
match()->forFirst()
tofindFirst()
#70
-
- Enhancements
- When no automatic delimiter (
/
,#
,%
,~
, etc.) is applicable, character0x01
is used (provided that it's not used anywhere else in the pattern). #71
- When no automatic delimiter (
- Features
- Added
match()->group()->findFirst()
#22 #70 - Added alternating groups in prepared patterns 🔥
Pattern::bind()
,Pattern::inject()
andPattern::prepare()
still receivestring
(as a user input) , but they can also receivestring[]
, which will be treated as a regex alternation group:is similar toPattern::bind('Choice: @values', [ 'values' => ['apple?', 'orange', 'pear'] ]);
Of coursePattern::of('Choice: (apple\?|orange|pear)')
'apple?'
and other values are protected against user-input malformed patterns.
- Added
- Bug fixes
- Previously, we added uniform quoting of
#
character on different PHP versions. Well, sorry to say that, we also made a bug doing that, when#
was also a delimiter. This bug is fixed now.
- Previously, we added uniform quoting of
- Breaking changes
- Renamed
CleanRegexException
toPatternException
- Moved
RegexException
to\TRegx
from\TRegx\CleanRegex\Exception
- Simplified the namespace of public exceptions:
- From
\TRegx\CleanRegex\Exception\CleanRegex
to\TRegx\CleanRegex\Exception
- From
- Renamed
- Enhancements
- Updated the hierarchy of public exceptions:
RegexException
PregException
(extendsRegexException
, instead of\Exception
)PatternException
IntegerFormatException
(extendsPatternException
, instead of\Exception
)NoFirstElementFluentException
(extendsPatternException
, instead of\Exception
)
- Previously,
RuntimePregException
was used to indicate every error that was reported bypreg_last_error()
. Now, the following subclasses ofRuntimePregException
are thrown:SubjectEncodingPregException
forPREG_BAD_UTF8_ERROR
Utf8OffsetPregException
forPREG_BAD_UTF8_OFFSET_ERROR
BacktrackLimitPregException
forPREG_BACKTRACK_LIMIT_ERROR
RecursionLimitPregException
forPREG_RECURSION_LIMIT_ERROR
JitStackLimitPregException
forPREG_JIT_STACKLIMIT_ERROR
- Updated the hierarchy of public exceptions:
- Features
-
Added
match()->groupBy()
/match()->filter()->groupBy()
:match()->groupBy()->texts()
match()->groupBy()->map(callable<Match>)
match()->groupBy()->flatMap(callable<Match>)
match()->groupBy()->offsets()
/byteOffsets()
when
groupBy()
is preceded byfilter()
, it will take indexes, limits, matches order and user data into account.
-
- Breaking changes
- Renamed exceptions:
SafeRegexException
toPregException
CompileSafeRegexException
toCompilePregException
RuntimeSafeRegexException
toRuntimePregException
SuspectedReturnSafeRegexException
toSuspectedReturnPregException
- Removed
pattern()->match()->iterate()
- it was only needed as a substitute forforEach()
, pre PHP 7, where methods couldn't be named with keywords.
- Renamed exceptions:
- Features
- Added
preg::last_error_msg()
, which works likepreg::last_error()
, but returns a human-readable message, instead ofint
.
- Added
- Fixing PHP
preg_match()
in some cases returns2
, instead of1
. T-Regx fixes this bug by always returning1
, on every PHP version (https://bugs.php.net/bug.php?id=78853).
- Breaking changes
- Methods
pattern()
/Pattern::of()
no longer "magically" guess whether a pattern is delimited or not.Pattern::of()
assumes pattern is delimited, newPattern::pcre()
takes an old-school delimited pattern. - Constructor
new Pattern()
is no longer a part of T-Regx API. UsePattern::of()
/pattern()
- Renamed
Match.parseInt()
toMatch.toInt()
(the same forMatchGroup
) - Removed
pattern()->match()->test()
/fails()
. From now on, usepattern()->test()
/fails()
- Removed
is()
:is()->delimited()
is()->usable()
is()->valid()
is changed tovalid()
- Removed
split()->ex()
, changedsplit()->inc()
tosplit()
- Methods
- Features
- Added
Match.group().replace()
🔥 - Added
pattern()->match()->fluent()
🔥 - Added
pattern()->match()->asInt()
- Added
pattern()->match()->distinct()
(leaves only unique matches) - Added prepared pattern method
Pattern::inject()
/Pattern::bind()
(see below) - In
pattern()->match()->groups()
:- Added
groups()->forEach()
/iterate()
- Added
groups()->flatMap()
- Added
groups()->map()
- Added
group()->fluent()
- Added
groups()->names()
(andnamedGroups()->names()
) - Added
groups()->count()
(andnamedGroups()->count()
)
- Added
- Added
match()->offsets()->fluent()
- Added
match()->group(string)->offsets()->fluent()
- Added
pattern()->forArray()->strict()
which throws for invalid values, instead of filtering them out - Added
pattern()->replace()->counting()
- Added
- SafeRegex
- Added
preg::grep_keys()
🔥, that works exactly likepreg::grep()
, but filters by keys (also acceptsPREG_GREP_INVERT
)
- Added
- Enhancements/updates
- Method
by()->group()->orElse()
now receives lazy-loadedMatch
, instead of a subject - Added
withReferences()
toCompositePattern.chainedReplace()
- Previously named
Pattern::inject()
is renamed toPattern::bind()
- The
Pattern::bind()
(oldPattern::inject()
) still accepts values as an associative array, but newPattern::inject()
receives values without regard for the keys. - Fixed passing invalid types to
forArray()
. Previously, caused fatal error due to internalpreg_grep()
implementation.
- Method
- Other
- Now
MalformedPatternException
is thrown, instead ofCompileSafeRegexException
, when using invalid PCRE syntax. - Returning
Match
fromreplace()->callback()
(instead ofMatch.text()
asstring
) - Match
+12
is no longer considered a valid integer forisInt()
/toInt()
- Unnamed group will be represented as
null
inMatch.groupNames()
, instead of being simply ignored - helper
pattern()
method,Pattern
andPatternBuilder
now returnPatternInterface
, instead ofPattern
class.Pattern
class now only holds static utility methods, andPatternImpl
holds the pattern implementation.
- Now
- Maintenance
- PhpUnit throws different exceptions because
of PHP
__toString()
exception policy change.
- PhpUnit throws different exceptions because
of PHP
Footnote:
- Apart from PHP type hints, every version up to this point could be run on PHP 5.3 (if one removes type hints from code, one can run T-Regx on PHP 5.3). Every error, exception, malfunction, inconsistency was handled correctly by T-Regx. From this version on (0.9.2), handling of the errors and inconsistencies is dropped, since T-Regx now only supports PHP 7.1.
- Features
- Added
Match.textLength()
- Added
Match.group().textLength()
- Added
Match.groupsCount()
- Added:
by()->group()->orIgnore()
by()->group()->orElse()
by()->group()->callback()
which acceptsMatchGroup
as an argument
- Added
- Features
- Pass flags as
pattern()
second argument - Add
Match.groups()
- Add
Match.limit()
- Add
Match.group()->all()
Match.getUserData()
/setUserData()
- Add
ReplaceMatch.modifiedSubject()
- Returning from
match()->first(callable)
modifies its return value - Add
pattern()->remove()
- Add
pattern()->replace()->by()
- Add
match()->only(int)
- Add
match()->flatMap()
- Add
match()->group()->all()
/first()
/only()
- Add
match()->iterator()
- Add
match()->forFirst()
- with methods
orReturn()
,orElse()
andorThrow()
orThrow()
can instantiate exceptions by class name (with one of predefined constructor signatures)
- with methods
match->only(i)
callspreg_match()
fori=1
, andpreg_match_all()
for other valuespattern()->match()
is\Countable
- Add UTF-8 support for methods
offset()
,modifiedOffset()
andmodifiedSubject()
- Add
split()->filter()
- Add
NotMatched.groupsCount()
- Add
CompositePattern
(#8) - Add
PatternBuilder
withprepare()
,inject()
andcompose()
methods (#25)
- Add UTF-8 support for methods
- Use
PREG_UNMATCHED_AS_NULL
if PHP version is supported
- Pass flags as
- Tests
- Split tests into
\Test\Unit
,\Test\Integration
,\Test\Functional
and\Test\Feature
folders - Add dynamic skip for
ErrorsCleanerTest
- Handle PHP bugfix in 7.1.13.
- Split tests into
- Other
- Set
\TRegx
namespace prefix - Add
ext-mbstring
requirement tocomposer.json
. preg_match()
won't return unmatched groups at the end of list, which makes validating groups and general work with group names impossible. Then, a second call topreg_match_all()
is done to get a list of all groups (even unmatched ones). The call topreg_match_all()
is of course only in the case ofhasGroup()
or similar method. Regular methods likeMatch.text()
won't callpreg_match_all()
- Set
- Debug
- Add
pregConstant
field toRuntimeError
. Only reason to do it is so if you catch the exception it in debugger, you'll see the constant name (i.e.PREG_BAD_UTF8_ERROR
) instead of the constant value (i.e.4
). - Handle bug PHP #75355
- Add
- Bug fixes
preg::replace()
andpreg::filter()
only consider[]
error-prone if input subject was also an empty array.