Simple library for regular expressions in PHP.
😎 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.
Pattern::pcre()
, so it's more concise, and doesn't use builder()
method.Pattern::alteration()
which allows building Pattern
with just an alteration group.
Pattern::alteration(['foo', 'bar'])
is /(?:foo|bar)/
Pattern::template()->alteration()
false
as an alteration value didn't throw \InvalidArgumentException
.The detailed list of changes is also in ChangeLog.md.
Rawrrrr!
😎 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:
/\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.
Pattern::inject('()(?)')
failed parsing\c\
as invalid entity, all T-Regx entry points correctly recognize it as valid\Q\
as invalid entity, all T-Regx entry points correctly recognize it as valid(?#\
as invalid entity, all T-Regx entry points correctly recognize it as valid#\
as invalid entity in X
tended mode, all T-Regx entry points correctly recognize it as validOptional.map()
, which resembles Java 8 optionals.pattern()->match()->asInt()->findFirst()->orElse()
receive NotMatched
argumentpattern()->match()->asInt()->findNth()->orElse()
receive NotMatched
argumentpattern()->match()->offsets()->findFirst()->orElse()
receive NotMatched
argumentpattern()->match()->offsets()->findNth()->orElse()
receive NotMatched
argumentThe detailed list of changes is also in ChangeLog.md.
Rawrrrr!
😎 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
Pattern::compose()->allMatch()
was horrible, we renamed it to testAll()
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()
:)
Pattern::compose()->chainedRemove()
to prune()
, because "chained remove" was basically prune, just with a more scary name 😨Features
Pattern::compose()->failAny()
, returning true
if any of the patterns didn't match the subjectPattern::compose()->failAll()
, returning true
if all the patterns didn't match the subjectSame 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!
😎 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.
Pattern::template()
to Pattern::builder()
. So if you need to build many elements, you can go
Pattern::builder($yourTemplateHere)
->literal($value)
->mask($mask, $keys)
->build();
Pattern::template()
, which works similarly to Pattern::builder()
but allows only one chain:
$pattern = Pattern::template('^@$')->literal('Hi!'); // that's it
The detailed list of changes is also in ChangeLog.md.
Rawrrrr!
😎 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.
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()
:
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
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!
😎 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.
orElseCalling()
didn't throw InvalidReturnValueException
FluentMatchPattern
to Stream
, similar to Java 8 streamsfluent()
to stream()
, similar to Java 8 streamsNoSuchElementFluentException
to NoSuchStreamElementException
IntStream.stream()
The detailed list of changes is also in ChangeLog.md.
Rawrrrr!
😎 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:
Pattern.match().offsets()
. Use Pattern.match().map()
.Pattern.match().group().offsets()
. Use Pattern.match().group().map()
.Pattern.split()
, previously unmatched separators were represented as an empty string
. Now, they're represented as null
.flatMap()
, see details in ChangeLog.md.tuple()
and triple()
, see ChangeLog.md.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.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.Optional.get()
. See details in ChangeLog.md.Pattern.match()
, see ChangeLog.md.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!
😎 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.
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:
'1'
and true
are no longer considered equal''
and false
are no longer considered equal0
and false
are no longer considered equal1
and true
are no longer considered equal0
and '0'
are no longer considered equalnull
and false
are no longer considered equalFor 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()
.
Stream.values().keys().first()
didn't always reindex to 0
.stream()->asInt()
was allowed, but usingDuplicateName()
groups weren't.
Now both kinds of groups are correctly passed into stream()->asInt()
.groupByCallback()
was allowed, but usingDuplicateName()
groups
weren't.
Now both kinds of groups are correctly passed into groupByCallback()
.Detail.group().or()
which behaves similarly to orReturn()
but only accepts a non-nullable string
.Stream.limit()
, which limits elements present in a stream from the endStream.skip()
, which limits elements present in a stream from the startIntStream.limit()
, which limits elements present in a stream from the endIntStream.skip()
, which limits elements present in a stream from the startStream.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!
😎 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.
Optional.orThrow()
accepted exception class name as string
. Currently, orThrow()
accepts an instance of \Throwable
.ClassExpectedException
, which was thrown when an invalid class name was passed to orThrow()
.NoSuitableConstructorException
, which was thrown when an invalid class was passed to orThrow()
.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()
.Detail.setUserData()
/getUserData()
match()->groupByCallback()
didn't throw InvalidReturnValueException
for invalid group
valueDetail.limit()
The detailed list of changes is also in ChangeLog.md.
Rawrrrr!
😎 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.
GroupLimit
to GroupMatch
.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
match()->offsets()
and match()->group()->offsets()
now return offsets as characters. Previously they returned
them as bytes.SubjectNotMatchedException.getSubject()
The detailed list of changes is also in ChangeLog.md.
Rawrrrr!