Skip to content

Commit 1a81d68

Browse files
authored
Merge pull request #5 from MaestroError/update-documentation-and-add-advanced-usage-section
Update documentation and add advanced usage section
2 parents 3346861 + 0ae2054 commit 1a81d68

15 files changed

+984
-111
lines changed

README.md

Lines changed: 707 additions & 52 deletions
Large diffs are not rendered by default.

src/Builder.php

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class Builder implements BuilderContract {
6767
FilePathWinPattern::class,
6868
];
6969

70+
protected bool $returnGroups = false;
71+
7072
/**
7173
* Constructs a new Builder instance.
7274
*
@@ -131,7 +133,48 @@ protected function validateAsString(): bool {
131133
* @return array|null An array of matches or null if no matches are found.
132134
*/
133135
protected function getAllMatches(): ?array {
134-
return $this->pattern->getMatches($this->str);
136+
if ($this->getReturnGroups()) {
137+
// Get unfiltered matches and groups
138+
$resultsArray = $this->pattern->getMatches($this->str, true);
139+
if ($resultsArray["results"]) {
140+
// Get array of associative arrays with "result" and "groups" keys
141+
$groupedResults = $this->buildResultByGroup($resultsArray["results"], $resultsArray["groups"]);
142+
return $groupedResults;
143+
} else {
144+
return null;
145+
}
146+
} else {
147+
return $this->pattern->getMatches($this->str);
148+
}
149+
}
150+
151+
/**
152+
* Build results array from filtered matches and groups.
153+
*
154+
* @param $matches filtered array from "preg_match_all", but with original indexes.
155+
* @param $groups array of captured groups from "preg_match_all".
156+
* @return array An array of matches and their captured groups.
157+
*/
158+
protected function buildResultByGroup(array $matches, array $groups): array {
159+
// Empty array for grouped result
160+
$groupedResults = [];
161+
// Loop over only matches
162+
foreach ($matches as $index => $value) {
163+
// Add match as result
164+
$matchArray = [
165+
"result" => $value,
166+
];
167+
// Use match index to get it's groups
168+
$capturedGroupsForThisMatch = [];
169+
foreach ($groups as $groupArray) {
170+
$capturedGroupsForThisMatch[] = $groupArray[$index];
171+
}
172+
// Add captured groups under "groups" key
173+
$matchArray["groups"] = $capturedGroupsForThisMatch;
174+
// Add array to result
175+
$groupedResults[] = $matchArray;
176+
}
177+
return $groupedResults;
135178
}
136179

137180
/**
@@ -268,6 +311,15 @@ public function asSticky(): self {
268311
return $this;
269312
}
270313

314+
public function setReturnGroups(bool $enable): self {
315+
$this->returnGroups = $enable;
316+
return $this;
317+
}
318+
319+
public function getReturnGroups(): bool {
320+
return $this->returnGroups;
321+
}
322+
271323
/**
272324
* Dynamically handles calls to pattern methods.
273325
*

src/Contracts/BuilderContract.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ public function registerPatterns(array $patterns): self;
8484
*/
8585
public function getPatterns(): array;
8686

87+
/**
88+
* Sets returnGroups property
89+
*
90+
* @return self Returns the Builder instance.
91+
*/
92+
public function setReturnGroups(bool $enable): self;
93+
94+
/**
95+
* Gets returnGroups property
96+
*
97+
* @return self Returns the Builder instance.
98+
*/
99+
public function getReturnGroups(): bool;
100+
87101
/**
88102
* Magic method to handle dynamic method calls.
89103
*

src/Contracts/PatternContract.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ public function validateMatches(string $input): bool;
5454
* Returns all matches found by this pattern.
5555
*
5656
* @param string $input The input string to validate.
57+
* @param bool $returnGroups if true returns whole array of matches (including groups).
5758
* @return array all matches found in input.
5859
*/
59-
public function getMatches(string $input): ?array;
60+
public function getMatches(string $input, bool $returnGroups = false): ?array;
6061

6162
/**
6263
* Generates the regex pattern for input validation.

src/Options/SpecificCurrenciesOption.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,29 @@ public function setSpecificCurrencies(array|string $currencies): self {
4040
return $this;
4141
}
4242

43-
public function onlyUSD($cur = true) {
44-
if ($cur) {
43+
public function onlyUSD($only = true) {
44+
if ($only) {
4545
$this->specificCurrencies = ["$"];
4646
}
4747
return $this;
4848
}
4949

50-
public function onlyEUR($cur = true) {
51-
if ($cur) {
50+
public function onlyEUR($only = true) {
51+
if ($only) {
5252
$this->specificCurrencies = [""];
5353
}
5454
return $this;
5555
}
5656

57-
public function onlyGBP($cur = true) {
58-
if ($cur) {
57+
public function onlyGBP($only = true) {
58+
if ($only) {
5959
$this->specificCurrencies = ["£"];
6060
}
6161
return $this;
6262
}
6363

64-
public function onlyGEL($cur = true) {
65-
if ($cur) {
64+
public function onlyGEL($only = true) {
65+
if ($only) {
6666
$this->specificCurrencies = [""];
6767
}
6868
return $this;

src/OptionsMapper.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,44 +35,59 @@ class OptionsMapper {
3535
"minLength" => [LengthOption::class, "minLength"],
3636
"maxLength" => [LengthOption::class, "maxLength"],
3737
"length" => [LengthOption::class, "exactLength"],
38+
3839
"minNumbers" => [NumberOption::class, "setMinValue"],
3940
"maxNumbers" => [NumberOption::class, "setMaxValue"],
4041
"minDigits" => [NumberOption::class, "setMinValue"],
4142
"maxDigits" => [NumberOption::class, "setMaxValue"],
4243
"numberAmount" => [NumberOption::class, "setExactValue"],
43-
"allowChars" => [CharacterOption::class, "allow"],
44+
45+
"onlyChars" => [CharacterOption::class, "allow"],
4446
"excludeChars" => [CharacterOption::class, "exclude"],
4547
"minUppercase" => [CharacterOption::class, "minUppercase"],
4648
"minLowercase" => [CharacterOption::class, "minLowercase"],
47-
"validIPv6" => [IPv6Option::class, "validIPv6"],
49+
4850
"minSpecialChars" => [CharOption::class, "minSpecialCharacters"],
4951
"maxSpecialChars" => [CharOption::class, "maxSpecialCharacters"],
5052
"onlyLowercase" => [CharOption::class, "onlyLowercase"],
5153
"onlyUppercase" => [CharOption::class, "onlyUppercase"],
54+
"noSpecialChars" => [CharOption::class, "noSpecialCharacters"],
55+
56+
"validIPv6" => [IPv6Option::class, "validIPv6"],
57+
5258
"isFile" => [FileOption::class, "isFile"],
5359
"isDirectory" => [FileOption::class, "isDirectory"],
60+
5461
"fileExists" => [FileExistsOption::class, "fileExists"],
62+
5563
"specificCurrencies" => [SpecificCurrenciesOption::class, "setSpecificCurrencies"],
5664
"onlyUSD" => [SpecificCurrenciesOption::class, "onlyUSD"],
5765
"onlyEUR" => [SpecificCurrenciesOption::class, "onlyEUR"],
5866
"onlyGBP" => [SpecificCurrenciesOption::class, "onlyGBP"],
5967
"onlyGEL" => [SpecificCurrenciesOption::class, "onlyGEL"],
68+
6069
"pathType" => [PathTypeOption::class, "setPathType"],
70+
6171
"countryCode" => [CountryCodeOption::class, "setCountryCode"],
62-
"noSpecialChars" => [CharOption::class, "noSpecialCharacters"],
72+
6373
"noSpaces" => [ContainSpacesOption::class, "noSpaces"],
6474
"noDoubleSpaces" => [ContainSpacesOption::class, "noDoubleSpaces"],
6575
"maxSpaces" => [ContainSpacesOption::class, "maxSpaces"],
76+
6677
"onlyDomains" => [DomainSpecificOption::class, "setAllowedDomains"],
6778
"onlyExtensions" => [DomainSpecificOption::class, "setAllowedExtensions"],
79+
6880
"onlyProtocol" => [ProtocolOption::class, "onlyProtocol"],
6981
"onlyHttp" => [ProtocolOption::class, "onlyHttp"],
7082
"onlyHttps" => [ProtocolOption::class, "onlyHttps"],
83+
7184
"onlyVisa" => [CardTypeOption::class, "onlyVisa"],
7285
"onlyMasterCard" => [CardTypeOption::class, "onlyMasterCard"],
7386
"onlyAmex" => [CardTypeOption::class, "onlyAmex"],
7487
"cardTypes" => [CardTypeOption::class, "allowCardTypes"],
88+
7589
"onlyAlphanumeric" => [OnlyAlphanumericOption::class, "onlyAlphanumeric"],
90+
7691
"onlyTags" => [HtmlTagsOption::class, "allowTags"],
7792
"restrictTags" => [HtmlTagsOption::class, "restrictTags"],
7893
];

src/Patterns/BasePattern.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,28 @@ public function validateMatches(string $input): bool {
122122
* @param string $input The input string to search for matches.
123123
* @return array An array of matches.
124124
*/
125-
public function getMatches(string $input): ?array {
125+
public function getMatches(string $input, bool $returnGroups = false): ?array {
126126
$mainPattern = $this->getMatchesValidationPattern();
127127
preg_match_all($mainPattern, $input, $matches);
128128

129129
if (!$matches[0]) {
130130
return null;
131131
}
132-
// Filter matches based on each option
133-
return $this->filterByOptions($matches[0]);
132+
133+
if ($returnGroups) {
134+
// Filter matches but keep indexes same
135+
$results = $this->filterByOptions($matches[0], false);
136+
// Unset matches and keep only groups
137+
unset($matches[0]);
138+
$groups = $matches;
139+
return [
140+
"results" => $results,
141+
"groups" => $groups
142+
];
143+
} else {
144+
// Filter matches based on each option
145+
return $this->filterByOptions($matches[0]);
146+
}
134147
}
135148

136149
/**
@@ -139,11 +152,17 @@ public function getMatches(string $input): ?array {
139152
* @param array $allMatches Array of matches to be filtered.
140153
* @return array Filtered array of matches.
141154
*/
142-
protected function filterByOptions(array $allMatches): array {
155+
protected function filterByOptions(array $allMatches, $fixArrayIndexes = true): array {
143156
// Use array_filter to keep only those matches that pass all options' validation
144-
return array_values(array_filter($allMatches, function($match) {
157+
$filtered = array_filter($allMatches, function($match) {
145158
return $this->validateOptions($match);
146-
}));
159+
});
160+
161+
if ($fixArrayIndexes) {
162+
return array_values($filtered);
163+
} else {
164+
return $filtered;
165+
}
147166
}
148167

149168
/**

src/Patterns/BuilderPattern.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class BuilderPattern extends BasePattern {
3131
*/
3232
protected bool $lazy = false;
3333

34+
/**
35+
* @var bool Flag to indicate that pattern is used inside charSet (Auto remove of extra "[]").
36+
*/
37+
protected bool $inCharSet = false;
38+
3439
/**
3540
* @var BuilderContract Reference to the main Builder object.
3641
*/
@@ -95,27 +100,27 @@ private function applyQuantifier(string $pattern, string|null $q): string {
95100
}
96101

97102
if ($q == 'zeroOrMore' || $q == '0>' || $q == '0+' || $q == '*') {
98-
$p = "(" . $pattern . ')*';
103+
$p = "(?:" . $pattern . ')*';
99104
return $this->lazy ? $this->addLazy($p) : $p;
100105
} elseif ($q == 'oneOrMore' || $q == '1>' || $q == '1+' || $q == '+') {
101-
$p = "(" . $pattern . ')+';
106+
$p = "(?:" . $pattern . ')+';
102107
return $this->lazy ? $this->addLazy($p) : $p;
103108
} elseif ($q == 'optional' || $q == '?' || $q == '|') {
104-
$p = "(" . $pattern . ')?';
109+
$p = "(?:" . $pattern . ')?';
105110
return $this->lazy ? $this->addLazy($p) : $p;
106111
}
107112

108113
if (is_int($q)) {
109-
$p = "(" . $pattern . "){".$q."}";
114+
$p = "(?:" . $pattern . "){".$q."}";
110115
return $this->lazy ? $this->addLazy($p) : $p;
111116
} elseif (preg_match("/^\d{1,10}$/", $q)) {
112-
$p = "(" . $pattern . '){'.$q.'}';
117+
$p = "(?:" . $pattern . '){'.$q.'}';
113118
return $this->lazy ? $this->addLazy($p) : $p;
114119
} elseif (preg_match("/^\d{1,10},\d{1,10}$/", $q)) {
115120
$range = explode(",", $q);
116121
$f = $range[0];
117122
$s = $range[1];
118-
$p = "(" . $pattern . ")" . "{" . $f . "," . $s ."}";
123+
$p = "(?:" . $pattern . ")" . "{" . $f . "," . $s ."}";
119124
return $this->lazy ? $this->addLazy($p) : $p;
120125
}
121126

@@ -134,7 +139,7 @@ private function getLengthOption(int|null $length = null, int $minLength = 0, in
134139
if (is_int($length) && $length > 0) {
135140
$qntf = "{" . $length . "}";
136141
return $this->lazy ? $this->addLazy($qntf) : $qntf;
137-
} elseif ($length === 0) {
142+
} elseif ($length === 0 || $this->inCharSet) {
138143
return "";
139144
}
140145

@@ -175,5 +180,10 @@ public function lazy(): self {
175180
return $this;
176181
}
177182

183+
public function inCharSet(): self {
184+
$this->inCharSet = true;
185+
return $this;
186+
}
187+
178188

179189
}

0 commit comments

Comments
 (0)