Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
759 changes: 707 additions & 52 deletions README.md

Large diffs are not rendered by default.

54 changes: 53 additions & 1 deletion src/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class Builder implements BuilderContract {
FilePathWinPattern::class,
];

protected bool $returnGroups = false;

/**
* Constructs a new Builder instance.
*
Expand Down Expand Up @@ -131,7 +133,48 @@ protected function validateAsString(): bool {
* @return array|null An array of matches or null if no matches are found.
*/
protected function getAllMatches(): ?array {
return $this->pattern->getMatches($this->str);
if ($this->getReturnGroups()) {
// Get unfiltered matches and groups
$resultsArray = $this->pattern->getMatches($this->str, true);
if ($resultsArray["results"]) {
// Get array of associative arrays with "result" and "groups" keys
$groupedResults = $this->buildResultByGroup($resultsArray["results"], $resultsArray["groups"]);
return $groupedResults;
} else {
return null;
}
} else {
return $this->pattern->getMatches($this->str);
}
}

/**
* Build results array from filtered matches and groups.
*
* @param $matches filtered array from "preg_match_all", but with original indexes.
* @param $groups array of captured groups from "preg_match_all".
* @return array An array of matches and their captured groups.
*/
protected function buildResultByGroup(array $matches, array $groups): array {
// Empty array for grouped result
$groupedResults = [];
// Loop over only matches
foreach ($matches as $index => $value) {
// Add match as result
$matchArray = [
"result" => $value,
];
// Use match index to get it's groups
$capturedGroupsForThisMatch = [];
foreach ($groups as $groupArray) {
$capturedGroupsForThisMatch[] = $groupArray[$index];
}
// Add captured groups under "groups" key
$matchArray["groups"] = $capturedGroupsForThisMatch;
// Add array to result
$groupedResults[] = $matchArray;
}
return $groupedResults;
}

/**
Expand Down Expand Up @@ -268,6 +311,15 @@ public function asSticky(): self {
return $this;
}

public function setReturnGroups(bool $enable): self {
$this->returnGroups = $enable;
return $this;
}

public function getReturnGroups(): bool {
return $this->returnGroups;
}

/**
* Dynamically handles calls to pattern methods.
*
Expand Down
14 changes: 14 additions & 0 deletions src/Contracts/BuilderContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ public function registerPatterns(array $patterns): self;
*/
public function getPatterns(): array;

/**
* Sets returnGroups property
*
* @return self Returns the Builder instance.
*/
public function setReturnGroups(bool $enable): self;

/**
* Gets returnGroups property
*
* @return self Returns the Builder instance.
*/
public function getReturnGroups(): bool;

/**
* Magic method to handle dynamic method calls.
*
Expand Down
3 changes: 2 additions & 1 deletion src/Contracts/PatternContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ public function validateMatches(string $input): bool;
* Returns all matches found by this pattern.
*
* @param string $input The input string to validate.
* @param bool $returnGroups if true returns whole array of matches (including groups).
* @return array all matches found in input.
*/
public function getMatches(string $input): ?array;
public function getMatches(string $input, bool $returnGroups = false): ?array;

/**
* Generates the regex pattern for input validation.
Expand Down
16 changes: 8 additions & 8 deletions src/Options/SpecificCurrenciesOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,29 @@ public function setSpecificCurrencies(array|string $currencies): self {
return $this;
}

public function onlyUSD($cur = true) {
if ($cur) {
public function onlyUSD($only = true) {
if ($only) {
$this->specificCurrencies = ["$"];
}
return $this;
}

public function onlyEUR($cur = true) {
if ($cur) {
public function onlyEUR($only = true) {
if ($only) {
$this->specificCurrencies = ["€"];
}
return $this;
}

public function onlyGBP($cur = true) {
if ($cur) {
public function onlyGBP($only = true) {
if ($only) {
$this->specificCurrencies = ["£"];
}
return $this;
}

public function onlyGEL($cur = true) {
if ($cur) {
public function onlyGEL($only = true) {
if ($only) {
$this->specificCurrencies = ["₾"];
}
return $this;
Expand Down
21 changes: 18 additions & 3 deletions src/OptionsMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,44 +35,59 @@ class OptionsMapper {
"minLength" => [LengthOption::class, "minLength"],
"maxLength" => [LengthOption::class, "maxLength"],
"length" => [LengthOption::class, "exactLength"],

"minNumbers" => [NumberOption::class, "setMinValue"],
"maxNumbers" => [NumberOption::class, "setMaxValue"],
"minDigits" => [NumberOption::class, "setMinValue"],
"maxDigits" => [NumberOption::class, "setMaxValue"],
"numberAmount" => [NumberOption::class, "setExactValue"],
"allowChars" => [CharacterOption::class, "allow"],

"onlyChars" => [CharacterOption::class, "allow"],
"excludeChars" => [CharacterOption::class, "exclude"],
"minUppercase" => [CharacterOption::class, "minUppercase"],
"minLowercase" => [CharacterOption::class, "minLowercase"],
"validIPv6" => [IPv6Option::class, "validIPv6"],

"minSpecialChars" => [CharOption::class, "minSpecialCharacters"],
"maxSpecialChars" => [CharOption::class, "maxSpecialCharacters"],
"onlyLowercase" => [CharOption::class, "onlyLowercase"],
"onlyUppercase" => [CharOption::class, "onlyUppercase"],
"noSpecialChars" => [CharOption::class, "noSpecialCharacters"],

"validIPv6" => [IPv6Option::class, "validIPv6"],

"isFile" => [FileOption::class, "isFile"],
"isDirectory" => [FileOption::class, "isDirectory"],

"fileExists" => [FileExistsOption::class, "fileExists"],

"specificCurrencies" => [SpecificCurrenciesOption::class, "setSpecificCurrencies"],
"onlyUSD" => [SpecificCurrenciesOption::class, "onlyUSD"],
"onlyEUR" => [SpecificCurrenciesOption::class, "onlyEUR"],
"onlyGBP" => [SpecificCurrenciesOption::class, "onlyGBP"],
"onlyGEL" => [SpecificCurrenciesOption::class, "onlyGEL"],

"pathType" => [PathTypeOption::class, "setPathType"],

"countryCode" => [CountryCodeOption::class, "setCountryCode"],
"noSpecialChars" => [CharOption::class, "noSpecialCharacters"],

"noSpaces" => [ContainSpacesOption::class, "noSpaces"],
"noDoubleSpaces" => [ContainSpacesOption::class, "noDoubleSpaces"],
"maxSpaces" => [ContainSpacesOption::class, "maxSpaces"],

"onlyDomains" => [DomainSpecificOption::class, "setAllowedDomains"],
"onlyExtensions" => [DomainSpecificOption::class, "setAllowedExtensions"],

"onlyProtocol" => [ProtocolOption::class, "onlyProtocol"],
"onlyHttp" => [ProtocolOption::class, "onlyHttp"],
"onlyHttps" => [ProtocolOption::class, "onlyHttps"],

"onlyVisa" => [CardTypeOption::class, "onlyVisa"],
"onlyMasterCard" => [CardTypeOption::class, "onlyMasterCard"],
"onlyAmex" => [CardTypeOption::class, "onlyAmex"],
"cardTypes" => [CardTypeOption::class, "allowCardTypes"],

"onlyAlphanumeric" => [OnlyAlphanumericOption::class, "onlyAlphanumeric"],

"onlyTags" => [HtmlTagsOption::class, "allowTags"],
"restrictTags" => [HtmlTagsOption::class, "restrictTags"],
];
Expand Down
31 changes: 25 additions & 6 deletions src/Patterns/BasePattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,28 @@ public function validateMatches(string $input): bool {
* @param string $input The input string to search for matches.
* @return array An array of matches.
*/
public function getMatches(string $input): ?array {
public function getMatches(string $input, bool $returnGroups = false): ?array {
$mainPattern = $this->getMatchesValidationPattern();
preg_match_all($mainPattern, $input, $matches);

if (!$matches[0]) {
return null;
}
// Filter matches based on each option
return $this->filterByOptions($matches[0]);

if ($returnGroups) {
// Filter matches but keep indexes same
$results = $this->filterByOptions($matches[0], false);
// Unset matches and keep only groups
unset($matches[0]);
$groups = $matches;
return [
"results" => $results,
"groups" => $groups
];
} else {
// Filter matches based on each option
return $this->filterByOptions($matches[0]);
}
}

/**
Expand All @@ -139,11 +152,17 @@ public function getMatches(string $input): ?array {
* @param array $allMatches Array of matches to be filtered.
* @return array Filtered array of matches.
*/
protected function filterByOptions(array $allMatches): array {
protected function filterByOptions(array $allMatches, $fixArrayIndexes = true): array {
// Use array_filter to keep only those matches that pass all options' validation
return array_values(array_filter($allMatches, function($match) {
$filtered = array_filter($allMatches, function($match) {
return $this->validateOptions($match);
}));
});

if ($fixArrayIndexes) {
return array_values($filtered);
} else {
return $filtered;
}
}

/**
Expand Down
24 changes: 17 additions & 7 deletions src/Patterns/BuilderPattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class BuilderPattern extends BasePattern {
*/
protected bool $lazy = false;

/**
* @var bool Flag to indicate that pattern is used inside charSet (Auto remove of extra "[]").
*/
protected bool $inCharSet = false;

/**
* @var BuilderContract Reference to the main Builder object.
*/
Expand Down Expand Up @@ -95,27 +100,27 @@ private function applyQuantifier(string $pattern, string|null $q): string {
}

if ($q == 'zeroOrMore' || $q == '0>' || $q == '0+' || $q == '*') {
$p = "(" . $pattern . ')*';
$p = "(?:" . $pattern . ')*';
return $this->lazy ? $this->addLazy($p) : $p;
} elseif ($q == 'oneOrMore' || $q == '1>' || $q == '1+' || $q == '+') {
$p = "(" . $pattern . ')+';
$p = "(?:" . $pattern . ')+';
return $this->lazy ? $this->addLazy($p) : $p;
} elseif ($q == 'optional' || $q == '?' || $q == '|') {
$p = "(" . $pattern . ')?';
$p = "(?:" . $pattern . ')?';
return $this->lazy ? $this->addLazy($p) : $p;
}

if (is_int($q)) {
$p = "(" . $pattern . "){".$q."}";
$p = "(?:" . $pattern . "){".$q."}";
return $this->lazy ? $this->addLazy($p) : $p;
} elseif (preg_match("/^\d{1,10}$/", $q)) {
$p = "(" . $pattern . '){'.$q.'}';
$p = "(?:" . $pattern . '){'.$q.'}';
return $this->lazy ? $this->addLazy($p) : $p;
} elseif (preg_match("/^\d{1,10},\d{1,10}$/", $q)) {
$range = explode(",", $q);
$f = $range[0];
$s = $range[1];
$p = "(" . $pattern . ")" . "{" . $f . "," . $s ."}";
$p = "(?:" . $pattern . ")" . "{" . $f . "," . $s ."}";
return $this->lazy ? $this->addLazy($p) : $p;
}

Expand All @@ -134,7 +139,7 @@ private function getLengthOption(int|null $length = null, int $minLength = 0, in
if (is_int($length) && $length > 0) {
$qntf = "{" . $length . "}";
return $this->lazy ? $this->addLazy($qntf) : $qntf;
} elseif ($length === 0) {
} elseif ($length === 0 || $this->inCharSet) {
return "";
}

Expand Down Expand Up @@ -175,5 +180,10 @@ public function lazy(): self {
return $this;
}

public function inCharSet(): self {
$this->inCharSet = true;
return $this;
}


}
Loading