From 2e0d787d27b3683ce5e9be97aeab6e8908d60dc8 Mon Sep 17 00:00:00 2001 From: maestroerror Date: Tue, 27 Feb 2024 18:20:39 +0400 Subject: [PATCH 01/10] fixed setOptions method and tests --- src/Builder.php | 4 +++- src/Contracts/BuilderContract.php | 2 +- tests/Feature/EloquentRegexTest.php | 21 +++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Builder.php b/src/Builder.php index 7c93db9..defead3 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -186,7 +186,7 @@ public function toRegex(): string { } // In cases when pattern doesn't allow setting the options (like BuilderPattern) - public function setOptions(array|callable $config): void { + public function setOptions(array|callable $config): self { // Check if the pattern is set if (!$this->patternIsSet()) { throw new \LogicException("Pattern must be set before setting options."); @@ -200,6 +200,8 @@ public function setOptions(array|callable $config): void { else if (is_callable($config)) { $this->processCallback($config); } + + return $this; } /** diff --git a/src/Contracts/BuilderContract.php b/src/Contracts/BuilderContract.php index d588f07..9db2b98 100644 --- a/src/Contracts/BuilderContract.php +++ b/src/Contracts/BuilderContract.php @@ -59,7 +59,7 @@ public function toRegex(): string; * @param array|callable $config An array of options (optionName => value (arg)) or a callback function to configure options. * @return void */ - public function setOptions(array|callable $config): void; + public function setOptions(array|callable $config): self; /** * Registers a single pattern in the Builder. diff --git a/tests/Feature/EloquentRegexTest.php b/tests/Feature/EloquentRegexTest.php index 8db7be6..e9d0b24 100644 --- a/tests/Feature/EloquentRegexTest.php +++ b/tests/Feature/EloquentRegexTest.php @@ -4,7 +4,7 @@ // Custom Pattern tests: -it('reproduces alt prefix pattern from HSA using facade', function () { +it('reproduces alt prefix pattern from HSA using wrapper', function () { $regex = EloquentRegex::builder()->start() ->exact("alt=") ->group(function ($pattern) { @@ -16,7 +16,7 @@ expect($regex)->toBe("alt\=(\"|')"); }); -it('reproduces hashtag prefix pattern from HSA using facade', function () { +it('reproduces hashtag prefix pattern from HSA using wrapper', function () { $regex = EloquentRegex::builder()->start() ->lookBehind(function ($pattern) { $pattern->charSet(function ($pattern) { @@ -27,7 +27,7 @@ expect($regex)->toBe('(?<=["\>\s])\#'); }); -it('reproduces Text suffix pattern from HSA using facade', function () { +it('reproduces Text suffix pattern from HSA using wrapper', function () { $regex = EloquentRegex::builder()->start() ->openAngleBracket()->slash()->alphanumericRange(0, 10)->closeAngleBracket() ->toRegex(); @@ -35,7 +35,7 @@ expect($regex)->toBe('\<\/[a-zA-Z0-9]{0,10}\>'); }); -it('constructs regex for simple email validation using facade', function () { +it('constructs regex for simple email validation using wrapper', function () { $regex = EloquentRegex::builder()->start() ->textLowercase() ->atSymbol() @@ -47,7 +47,7 @@ expect($regex)->toBe('[a-z]+@[a-z]+\.[a-z]{2,4}'); }); -it('constructs regex for URL validation using facade', function () { +it('constructs regex for URL validation using wrapper', function () { $regex = EloquentRegex::builder()->pattern() ->exact(['http', 'https']) ->colon() @@ -60,7 +60,7 @@ expect($regex)->toBe('(http|https)\:\/\/[a-zA-Z]+\.[a-zA-Z]+'); }); -it('constructs regex for specific phone number format using facade', function () { +it('constructs regex for specific phone number format using wrapper', function () { $regex = EloquentRegex::builder()->pattern(function ($p) { $p->openParenthesis()->digits(3)->closeParenthesis() ->space() @@ -70,7 +70,7 @@ expect($regex)->toBe('\(\d{3}\) \d{3}\-\d{4}'); }); -it('extracts dates in specific format from text using facade', function () { +it('extracts dates in specific format from text using wrapper', function () { $matches = EloquentRegex::customPattern("Meeting on 2021-09-15 and 2021-10-20") ->digits(4) ->dash() @@ -82,17 +82,18 @@ expect($matches)->toEqual(['2021-09-15', '2021-10-20']); }); -it('validates usernames in a string using facade', function () { +it('validates usernames in a string using wrapper and LengthOption', function () { $check = EloquentRegex::customPattern("Users: user_123, JohnDoe99") ->alphanumeric() ->underscore("?") ->digitsRange(0, 2) + ->end()->setOptions(["maxLength" => 15]) ->checkString(); expect($check)->toBeTrue(); }); -it('extracts hashtags from text using facade', function () { +it('extracts hashtags from text using wrapper', function () { $matches = EloquentRegex::start("#hello #world This is a #test") ->hash() ->text() @@ -101,7 +102,7 @@ expect($matches)->toEqual(['#hello', '#world', '#test']); }); -it('extracts secret coded messages from text using facade', function () { +it('extracts secret coded messages from text using wrapper', function () { $text = "Normal text {secret: message one} more text {secret: another hidden text} end"; $matches = EloquentRegex::start($text) ->lookBehind(function ($pattern) { From 69ea1e5c2010848479afb352d23ffe7e99250f56 Mon Sep 17 00:00:00 2001 From: maestroerror Date: Tue, 27 Feb 2024 20:04:13 +0400 Subject: [PATCH 02/10] Structure document is ready --- README.md | 3 + STRUCTURE.md | 252 +++++++++++++++++++++ src/Builder.php | 2 +- src/Patterns/BuilderPattern.php | 4 +- tests/Feature/BuilderPatternTest.php | 2 +- tests/Feature/EloquentRegexTest.php | 6 +- tests/Unit/Patterns/BuilderPatternTest.php | 2 + 7 files changed, 264 insertions(+), 7 deletions(-) create mode 100644 STRUCTURE.md diff --git a/README.md b/README.md index a324c79..2d114d3 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ Examples: `->exact("hello world", false, "1+")` - Write documentation (add credit for https://regexr.com/ and ChatGPT) - Add automated tests on PR creation or on marging to main branch ✔️ +- Make Tests for quantifiers (add grouping) +- Make quantifiers available for special chars + ##### Coming next - Implement string resolver pattern to use strings like "text(2)-digits()" (or "text:2-digits", or "text|2-digits") as pattern diff --git a/STRUCTURE.md b/STRUCTURE.md new file mode 100644 index 0000000..b810793 --- /dev/null +++ b/STRUCTURE.md @@ -0,0 +1,252 @@ +# EloquentRegex Structure + +EloquentRegex offers a fluent and intuitive interface for Laravel developers to construct and execute regular expressions (regex) with ease. This document outlines the core components and functionalities of the EloquentRegex package, including ready-to-use patterns, custom pattern creation, and the use of options for added assertions and filtering. The primary purpose of the document is to clarify the inner workings and help you to understand the package better. + +### Table of Contents + +- [EloquentRegex Structure](#eloquentregex-structure) + - [Ready-to-Use Patterns](#ready-to-use-patterns) + - [Custom Patterns](#custom-patterns) + - [Starting Points](#starting-points) + - [Options](#options) + - [Applying Options](#applying-options) + - [Options as Filters](#options-as-filters) + - [Options in Custom Patterns](#options-in-custom-patterns) + - [Regex Flags](#regex-flags) + - [Usage Structure](#usage-structure) +- [Conclusion](#conclusion) + +## Ready-to-Use Patterns + +The inclusion of ready-to-use patterns for common formats like Email, URL, and IP addresses significantly streamlines validation tasks. These patterns are a testament to EloquentRegex's goal of providing developers with a toolkit that simplifies common regex tasks, making code more readable and maintainable. By encapsulating the complexity of regex syntax for these common use cases, EloquentRegex enables developers to perform validations and extractions without needing to manually craft and test these patterns. These patterns can be used directly with minimal setup: + +```php +EloquentRegex::source("test@example.com")->email()->check(); +``` + +They are defined in `src\Patterns` and I tried a lot to make pattern creation process as easy as possible. Here you can check example pattern: + +```php + 5 + ]; +} + +``` + +All available patterns are registered in `src\Builder.php`, inside `$patterns` property + +## Custom Patterns + +_Custom patterns allow for detailed and specific regex definitions._ + +The ability to create custom patterns using methods is central to EloquentRegex's value proposition. This feature leverages a fluent API, allowing developers to construct complex regex expressions through a series of method calls. Each method call builds upon the previous one, enabling the construction of regex patterns in a piecewise, understandable manner. This approach not only makes the creation of regex patterns more intuitive but also enhances code readability by making the patterns' purposes and structures clear at a glance. + +```php +EloquentRegex::start("#hello #world This is a #test")->hash()->text()->get(); +``` + +This functionality mainly depends on the `src\Patterns\BuilderPattern.php`, which technichally is one of the patterns in the system, just haven't predefined regex and allows you to build regex pattern using the chainable methods. + +### Starting Points + +Custom pattern can be initiated with `start` or `customPattern` methods, which are interchangeable and mark the beginning of a custom pattern creation. The `source` and the `string` methods are starting point for ready-to-use patterns, while it returns the main `Builder` class, you can "switch" to the custom pattern from it, of course, if you preffer syntax like this: + +```php +EloquentRegex::source("#hello #world This is a #test")->start()->hash()->text()->end()->get(); // end() method is optional here +``` + +## Options + +Options provide a powerful way to fine-tune patterns. They can be specific to the pattern or applied globally as extra assertions. Patterns define their arguments for easy option application. + +It is quiet easy to add a new option class, mainly it needs 2 methods `validate` and `build`. Validate method is used for validation and build method is optional for cases, where option uses regex for validation (Yes, some options are validating input using PHP methods, some Regex pattern and some both, it depends on the Option). Also, option classes need the "option methods", which should be registered as separated options in the `src\OptionsMapper.php` class. For example: + +```php +validateUsingRegex) { + return $this->validateUsingRegex($input); + } + + if ($this->isFile) { + if ($this->fileExtension) { + if (!preg_match("/\." . preg_quote($this->fileExtension) . "$/", $input)) { + return false; + } + } elseif (!preg_match("/\.[a-zA-Z0-9]+$/", $input)) { + return false; + } + } + + if ($this->isDirectory) { + if (substr($input, -1) != '/') { + return false; + } + } + + return true; + } + + // Builds regex pattern + public function build(): string { + if ($this->isFile) { + if ($this->fileExtension) { + return "[A-Za-z0-9\\/:\.\-\\\\]*\." . preg_quote($this->fileExtension); + } else { + return "[A-Za-z0-9\\/:\.\-\\\\]*\.[a-zA-Z0-9]+"; + } + } + + if ($this->isDirectory) { + return "(?:[a-zA-Z0-9\\/:\-\\\\]+)+"; + } + + return '.*'; + } + + // "option methods": + + public function isFile(string|null $extension = null) { + $this->isFile = true; + $this->fileExtension = $extension; + return $this; + } + + public function isDirectory(int $check = 1) { + $this->isDirectory = $check; + return $this; + } +} +``` + +Later, the "option methods" are registered in OptionsMapper class: + +```php +/** + * @var array Mapping of option names to their corresponding class and method. + */ +public static array $optionMethods = [ + // Other options... + "isFile" => [FileOption::class, "isFile"], + "isDirectory" => [FileOption::class, "isDirectory"], + // Other options... +]; +``` + +_Note: option methods **should** have only one argument._ + +### Applying Options + +- Via Arguments: Directly passing parameters defined by the pattern class in $args property. + +```php +EloquentRegex::source("StrongP@ssw0rd")->password(8, 1, 1, 1)->check(); +``` + +- Via Callback: Using a closure to apply multiple options dynamically (recomended way). + +```php +EloquentRegex::source("StrongP@ssw0rd")->password(function($pattern) { + return $pattern->minLength(8)->minUppercase(1)->minNumbers(1)->minSpecialChars(1); +})->check(); +``` + +- Via Array: Applying options using an associative array. + +```php +EloquentRegex::source("test@example.com")->email(['onlyExtensions' => ['com', 'org']])->check(); // true +``` + +_Note: argument includes pattern-specific option in predefined manner, but while using **array** or **callback** you can apply **any option** to **any pattern**_ + +### Options as Filters + +Options can also act as filters during extraction (`get()`), ensuring only matches that meet specific criteria are returned. + +```php +EloquentRegex::source("Visa: 4111 1111 1111 1111, MasterCard: 5500 0000 0000 0004, Amex: 3400 000000 00009") + ->creditCardNumber("visa, amex")->get(); +// Returns only Visa and Amex card numbers + +``` + +### Options in Custom Patterns + +Sure! You can apply option to your custom pattern using the `end(callback|array $config)` method: + +```php +EloquentRegex::customPattern("Users: user_123, JohnDoe_99") + ->alphanumeric() + ->underscore() + ->digitsRange(0, 5) + ->end(["maxLength" => 8]) // Applied maxLength option + ->get(); // Returns array including "user_123" within +``` + +### Regex Flags + +Regex flags are applied outside the options scope through easy-to-use methods, allowing for global pattern modifiers like Unicode support. + +```php +// Regex flags can be applied only after the end() method +EloquentRegex::start("მზადაა #1 ✔️ და #2 ✔️")->wordCharRange(0, 2)->end()->asUnicode()->get(); +``` + +## Usage structure + +Here you can check the usage structure: + +``` +[Initiator][Pattern][?Optional][Action] +``` + +- **Initiator** - Sets target string and returns `Builder` for ready-to-use patterns or `BuilderPattern` for building custom patterns (ex: `EloquentRegex::source`) +- **Pattern** - Method for ready to use pattern like `email`, `url`, `filePath` and etc. Or custom pattern created using the `BuilderPattern` methods. +- **?Optional** - Any optional methods like: regex expression flags and `end` method +- **Action** - Final method which performs some action like: `get`, `check`, `toRegex` and etc. + +# Conclusion + +EloquentRegex simplifies the creation and execution of regular expressions in Laravel applications. Through its intuitive API, developers can quickly implement complex regex operations with precision and flexibility. Whether utilizing ready-to-use patterns for common tasks or crafting custom solutions with dynamic options, EloquentRegex enhances productivity and code clarity. diff --git a/src/Builder.php b/src/Builder.php index defead3..686c07e 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -193,7 +193,7 @@ public function setOptions(array|callable $config): self { } // Handle options array scenario - if (is_array($config)) { + if (is_array($config) && !empty($config)) { $this->processConfigArray($config); } // Handle callback scenario diff --git a/src/Patterns/BuilderPattern.php b/src/Patterns/BuilderPattern.php index 5d6db8f..4a9b2d5 100644 --- a/src/Patterns/BuilderPattern.php +++ b/src/Patterns/BuilderPattern.php @@ -46,8 +46,8 @@ public function __construct(BuilderContract $builder = new Builder()) { // Builder class implementation methods START - public function end(): BuilderContract { - return $this->builder; // Return the Builder object + public function end(array|callable $config = []): BuilderContract { + return $this->builder->setOptions($config); // Return the Builder object } public function get(): ?array { diff --git a/tests/Feature/BuilderPatternTest.php b/tests/Feature/BuilderPatternTest.php index c2d29a9..7461c28 100644 --- a/tests/Feature/BuilderPatternTest.php +++ b/tests/Feature/BuilderPatternTest.php @@ -101,7 +101,7 @@ $check = $builder->start() ->alphanumeric() - ->underscore("?") + ->underscore() ->digitsRange(0, 2) ->checkString(); diff --git a/tests/Feature/EloquentRegexTest.php b/tests/Feature/EloquentRegexTest.php index e9d0b24..4d89f52 100644 --- a/tests/Feature/EloquentRegexTest.php +++ b/tests/Feature/EloquentRegexTest.php @@ -85,9 +85,9 @@ it('validates usernames in a string using wrapper and LengthOption', function () { $check = EloquentRegex::customPattern("Users: user_123, JohnDoe99") ->alphanumeric() - ->underscore("?") + ->underscore() ->digitsRange(0, 2) - ->end()->setOptions(["maxLength" => 15]) + ->end(["maxLength" => 15]) ->checkString(); expect($check)->toBeTrue(); @@ -150,7 +150,7 @@ it('validates a date format correctly', function () { $builder = EloquentRegex::string("2023-01-01"); - $check = $builder->date('Y-m-d')->check(); + $check = $builder->date()->check(); expect($check)->toBeTrue(); }); diff --git a/tests/Unit/Patterns/BuilderPatternTest.php b/tests/Unit/Patterns/BuilderPatternTest.php index d55adec..a3b701b 100644 --- a/tests/Unit/Patterns/BuilderPatternTest.php +++ b/tests/Unit/Patterns/BuilderPatternTest.php @@ -224,10 +224,12 @@ $builder->doubleQuote()->exact('quoted', true, '*')->doubleQuote(); $expectedPattern = '/"quoted*"/'; + // $expectedPattern = '/"(quoted)*"/'; // @todo Should return this $regex = $builder->getMatchesValidationPattern(); expect($regex)->toBe($expectedPattern); expect(preg_match($regex, '"quoted"'))->toBe(1); + // expect(preg_match($regex, '"quotedquotedquoted"'))->toBe(1); // @todo Should be true expect(preg_match($regex, '"quote"'))->toBe(1); // Should match as * allows for zero or more expect(preg_match($regex, 'quoted'))->toBe(0); // Should not match without quotes }); From 25a900fae3f84fcb17821bdc68935304cbe88bbd Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 16:27:34 +0400 Subject: [PATCH 03/10] quantifiers updated and created tests for them --- README.md | 6 +- STRUCTURE.md | 2 + src/Patterns/BuilderPattern.php | 72 +++++--- .../SpecificCharsTrait.php | 164 +++++++++--------- tests/Feature/EloquentRegexTest.php | 88 ++++++++++ tests/Unit/Patterns/BuilderPatternTest.php | 8 +- 6 files changed, 228 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 2d114d3..1e5139f 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ Examples: `->exact("hello world", false, "1+")` - Write documentation (add credit for https://regexr.com/ and ChatGPT) - Add automated tests on PR creation or on marging to main branch ✔️ -- Make Tests for quantifiers (add grouping) -- Make quantifiers available for special chars +- Make Tests for quantifiers (add grouping) ✔️ +- Make quantifiers available for special chars ✔️ ##### Coming next @@ -62,6 +62,6 @@ Examples: `->exact("hello world", false, "1+")` - Implement recursive pattern creation (Using "RI-321" string to create pattern matching this string) - Consider to add Postal Code Pattern - Make options controllable from config or provider (?) -- Make pattern controllable from config or provider (?) +- Make patterns controllable from config or provider (?) - I should be able to make new pattern using BuilderPattern - I should be able to make add custom pattern to the existing one using BuilderPattern diff --git a/STRUCTURE.md b/STRUCTURE.md index b810793..d548702 100644 --- a/STRUCTURE.md +++ b/STRUCTURE.md @@ -247,6 +247,8 @@ Here you can check the usage structure: - **?Optional** - Any optional methods like: regex expression flags and `end` method - **Action** - Final method which performs some action like: `get`, `check`, `toRegex` and etc. +_Note: Action methods have replicas in `BuilderPattern` to ensure that `end` method remains optional_ + # Conclusion EloquentRegex simplifies the creation and execution of regular expressions in Laravel applications. Through its intuitive API, developers can quickly implement complex regex operations with precision and flexibility. Whether utilizing ready-to-use patterns for common tasks or crafting custom solutions with dynamic options, EloquentRegex enhances productivity and code clarity. diff --git a/src/Patterns/BuilderPattern.php b/src/Patterns/BuilderPattern.php index 4a9b2d5..38b2e9d 100644 --- a/src/Patterns/BuilderPattern.php +++ b/src/Patterns/BuilderPattern.php @@ -87,19 +87,38 @@ public function getMatchesValidationPattern(): string { * @param string|null $quantifier The quantifier to apply. Can be 'zeroOrMore', 'oneOrMore', or 'optional'. * @return string The modified pattern with the quantifier applied. */ - private function applyQuantifier(string $pattern, string|null $quantifier): string { - switch ($quantifier) { - case 'zeroOrMore' || '0>' || '0+': - $p = $pattern . '*'; - return $this->lazy ? $this->addLazy($p) : $p; - case 'oneOrMore' || '1>' || '1+': - $p = $pattern . '+'; - return $this->lazy ? $this->addLazy($p) : $p; - case 'optional' || '?' || '|': - return $pattern . '?'; - default: - return $pattern; + private function applyQuantifier(string $pattern, string|null $q): string { + + if (!$q) { + return $pattern; + } + + if ($q == 'zeroOrMore' || $q == '0>' || $q == '0+' || $q == '*') { + $p = "(" . $pattern . ')*'; + return $this->lazy ? $this->addLazy($p) : $p; + } elseif ($q == 'oneOrMore' || $q == '1>' || $q == '1+' || $q == '+') { + $p = "(" . $pattern . ')+'; + return $this->lazy ? $this->addLazy($p) : $p; + } elseif ($q == 'optional' || $q == '?' || $q == '|') { + $p = "(" . $pattern . ')?'; + return $this->lazy ? $this->addLazy($p) : $p; } + + if (is_int($q)) { + $p = "(" . $pattern . "){".$q."}"; + return $this->lazy ? $this->addLazy($p) : $p; + } elseif (preg_match("/^\d{1,10}$/", $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 ."}"; + return $this->lazy ? $this->addLazy($p) : $p; + } + + return $pattern; } /** @@ -111,9 +130,11 @@ private function applyQuantifier(string $pattern, string|null $quantifier): stri * @return string The generated regex quantifier string. */ private function getLengthOption(int|null $length = null, int $minLength = 0, int $maxLength = 0): string { - if (is_int($length) && $length >= 0) { + if (is_int($length) && $length > 0) { $qntf = "{" . $length . "}"; return $this->lazy ? $this->addLazy($qntf) : $qntf; + } elseif ($length === 0) { + return ""; } if ($minLength > 0 && $maxLength > 0) { @@ -159,10 +180,11 @@ public function lazy(): self { * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. * @return self */ - public function charSet(callable $callback): self { + public function charSet(callable $callback, ?string $q = null): self { $subPattern = new self(); $callback($subPattern); - $this->pattern .= '[' . $subPattern->getPattern() . ']'; + $p = '[' . $subPattern->getPattern() . ']'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; return $this; } @@ -172,10 +194,11 @@ public function charSet(callable $callback): self { * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. * @return self */ - public function negativeCharSet(callable $callback): self { + public function negativeCharSet(callable $callback, ?string $q = null): self { $subPattern = new self(); $callback($subPattern); - $this->pattern .= '[^' . $subPattern->getPattern() . ']'; + $p = '[^' . $subPattern->getPattern() . ']'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; return $this; } @@ -185,10 +208,11 @@ public function negativeCharSet(callable $callback): self { * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. * @return self */ - public function group(callable $callback): self { + public function group(callable $callback, ?string $q = null): self { $subPattern = new self(); $callback($subPattern); - $this->pattern .= '(' . $subPattern->getPattern() . ')'; + $p = $subPattern->getPattern(); + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : '(' . $p . ')'; return $this; } @@ -198,10 +222,11 @@ public function group(callable $callback): self { * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. * @return self */ - public function nonCapturingGroup(callable $callback): self { + public function nonCapturingGroup(callable $callback, ?string $q = null): self { $subPattern = new self(); $callback($subPattern); - $this->pattern .= '(?:' . $subPattern->getPattern() . ')'; + $p = '(?:' . $subPattern->getPattern() . ')'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; return $this; } @@ -211,10 +236,11 @@ public function nonCapturingGroup(callable $callback): self { * @param callable $callback A callback that receives a BuilderPattern instance to define the alternation. * @return self */ - public function orPattern(callable $callback): self { + public function orPattern(callable $callback, ?string $q = null): self { $builder = new self(); $callback($builder); - $this->pattern .= '|' . $builder->getPattern(); + $p = $builder->getPattern(); + $this->pattern .= $q ? '|' . $this->applyQuantifier($p, $q) : '|' . $p; return $this; } diff --git a/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php b/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php index 4112f43..ad2325d 100644 --- a/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php +++ b/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php @@ -18,9 +18,9 @@ private function handleExact(string|array $string, $caseSensitive = true, $quant return $this; } - private function escapeAndAdd(string $char): self { + private function escapeAndAdd(string $char, $quantifier = null): self { $escapedChar = preg_quote($char, '/'); - $this->pattern .= $escapedChar; + $this->pattern .= $quantifier ? $this->applyQuantifier($escapedChar, $quantifier) : $escapedChar; return $this; } @@ -79,132 +79,132 @@ public function formFeed(): self { return $this; } - public function dash() { - return $this->escapeAndAdd("-"); + public function dash(string|null $q = null) { + return $this->escapeAndAdd("-", $q); } - public function dot(): self { - return $this->escapeAndAdd("."); // Matches dot "." character + public function dot(string|null $q = null): self { + return $this->escapeAndAdd(".", $q); // Matches dot "." character } - public function space() { - return $this->escapeAndAdd(" "); + public function space(string|null $q = null) { + return $this->escapeAndAdd(" ", $q); } - public function backslash(): self { - return $this->escapeAndAdd("\\"); + public function backslash(string|null $q = null): self { + return $this->escapeAndAdd("\\", $q); } - public function forwardSlash(): self { - return $this->escapeAndAdd("/"); + public function forwardSlash(string|null $q = null): self { + return $this->escapeAndAdd("/", $q); } - public function slash(): self { - return $this->escapeAndAdd("/"); + public function slash(string|null $q = null): self { + return $this->escapeAndAdd("/", $q); } - public function doubleSlash(): self { - return $this->escapeAndAdd("//"); + public function doubleSlash(string|null $q = null): self { + return $this->escapeAndAdd("//", $q); } - public function underscore(): self { - return $this->escapeAndAdd("_"); + public function underscore(string|null $q = null): self { + return $this->escapeAndAdd("_", $q); } - public function pipe(): self { - return $this->escapeAndAdd("|"); + public function pipe(string|null $q = null): self { + return $this->escapeAndAdd("|", $q); } - public function ampersand(): self { - return $this->escapeAndAdd("&"); + public function ampersand(string|null $q = null): self { + return $this->escapeAndAdd("&", $q); } - public function asterisk(): self { - return $this->escapeAndAdd("*"); + public function asterisk(string|null $q = null): self { + return $this->escapeAndAdd("*", $q); } - public function plus(): self { - return $this->escapeAndAdd("+"); + public function plus(string|null $q = null): self { + return $this->escapeAndAdd("+", $q); } - public function questionMark(): self { - return $this->escapeAndAdd("?"); + public function questionMark(string|null $q = null): self { + return $this->escapeAndAdd("?", $q); } - public function atSign(): self { - return $this->escapeAndAdd("@"); + public function atSign(string|null $q = null): self { + return $this->escapeAndAdd("@", $q); } - public function atSymbol(): self { - return $this->escapeAndAdd("@"); + public function atSymbol(string|null $q = null): self { + return $this->escapeAndAdd("@", $q); } - public function exclamationMark(): self { - return $this->escapeAndAdd("!"); + public function exclamationMark(string|null $q = null): self { + return $this->escapeAndAdd("!", $q); } - public function period(): self { - return $this->escapeAndAdd("."); + public function period(string|null $q = null): self { + return $this->escapeAndAdd(".", $q); } - public function comma(): self { - return $this->escapeAndAdd(","); + public function comma(string|null $q = null): self { + return $this->escapeAndAdd(",", $q); } - public function semicolon(): self { - return $this->escapeAndAdd(";"); + public function semicolon(string|null $q = null): self { + return $this->escapeAndAdd(";", $q); } - public function colon(): self { - return $this->escapeAndAdd(":"); + public function colon(string|null $q = null): self { + return $this->escapeAndAdd(":", $q); } - public function equalSign(): self { - return $this->escapeAndAdd("="); + public function equalSign(string|null $q = null): self { + return $this->escapeAndAdd("=", $q); } - public function tilde(): self { - return $this->escapeAndAdd("~"); + public function tilde(string|null $q = null): self { + return $this->escapeAndAdd("~", $q); } - public function hyphen(): self { - return $this->escapeAndAdd("-"); + public function hyphen(string|null $q = null): self { + return $this->escapeAndAdd("-", $q); } - public function minus(): self { - return $this->escapeAndAdd("-"); + public function minus(string|null $q = null): self { + return $this->escapeAndAdd("-", $q); } - public function doubleQuote(): self { - return $this->escapeAndAdd("\""); + public function doubleQuote(string|null $q = null): self { + return $this->escapeAndAdd("\"", $q); } - public function singleQuote(): self { - return $this->escapeAndAdd("'"); + public function singleQuote(string|null $q = null): self { + return $this->escapeAndAdd("'", $q); } - public function percent(): self { - return $this->escapeAndAdd("%"); + public function percent(string|null $q = null): self { + return $this->escapeAndAdd("%", $q); } - public function dollar(): self { - return $this->escapeAndAdd("$"); + public function dollar(string|null $q = null): self { + return $this->escapeAndAdd("$", $q); } - public function hash(): self { - return $this->escapeAndAdd("#"); + public function hash(string|null $q = null): self { + return $this->escapeAndAdd("#", $q); } - public function hashtag(): self { - return $this->escapeAndAdd("#"); + public function hashtag(string|null $q = null): self { + return $this->escapeAndAdd("#", $q); } - public function backtick(): self { - return $this->escapeAndAdd("`"); + public function backtick(string|null $q = null): self { + return $this->escapeAndAdd("`", $q); } - public function caret(): self { - return $this->escapeAndAdd("^"); + public function caret(string|null $q = null): self { + return $this->escapeAndAdd("^", $q); } public function unicode($code): self { @@ -215,48 +215,48 @@ public function unicode($code): self { // Methods for paired characters with separate open and close methods and an extra method with a boolean argument - public function openSquareBracket(): self { - return $this->escapeAndAdd("["); + public function openSquareBracket(string|null $q = null): self { + return $this->escapeAndAdd("[", $q); } - public function closeSquareBracket(): self { - return $this->escapeAndAdd("]"); + public function closeSquareBracket(string|null $q = null): self { + return $this->escapeAndAdd("]", $q); } public function squareBracket($isOpen = true): self { return $isOpen ? $this->openSquareBracket() : $this->closeSquareBracket(); } - public function openCurlyBrace(): self { - return $this->escapeAndAdd("{"); + public function openCurlyBrace(string|null $q = null): self { + return $this->escapeAndAdd("{", $q); } - public function closeCurlyBrace(): self { - return $this->escapeAndAdd("}"); + public function closeCurlyBrace(string|null $q = null): self { + return $this->escapeAndAdd("}", $q); } public function curlyBrace($isOpen = true): self { return $isOpen ? $this->openCurlyBrace() : $this->closeCurlyBrace(); } - public function openParenthesis(): self { - return $this->escapeAndAdd("("); + public function openParenthesis(string|null $q = null): self { + return $this->escapeAndAdd("(", $q); } - public function closeParenthesis(): self { - return $this->escapeAndAdd(")"); + public function closeParenthesis(string|null $q = null): self { + return $this->escapeAndAdd(")", $q); } public function parenthesis($isOpen = true): self { return $isOpen ? $this->openParenthesis() : $this->closeParenthesis(); } - public function openAngleBracket(): self { - return $this->escapeAndAdd("<"); + public function openAngleBracket(string|null $q = null): self { + return $this->escapeAndAdd("<", $q); } - public function closeAngleBracket(): self { - return $this->escapeAndAdd(">"); + public function closeAngleBracket(string|null $q = null): self { + return $this->escapeAndAdd(">", $q); } public function angleBracket($isOpen = true): self { diff --git a/tests/Feature/EloquentRegexTest.php b/tests/Feature/EloquentRegexTest.php index 4d89f52..8400f8e 100644 --- a/tests/Feature/EloquentRegexTest.php +++ b/tests/Feature/EloquentRegexTest.php @@ -239,3 +239,91 @@ expect($check)->toBeTrue(); }); + + +// Quantifier tests: +it('matches specific number of dashes', function () { + $result = EloquentRegex::builder()->pattern()->dash('?')->toRegex(); + expect($result)->toBe('(\-)?'); +}); + +it('matches optional dots', function () { + $result = EloquentRegex::builder()->pattern()->dot('?')->toRegex(); + expect($result)->toBe('(\.)?'); +}); + +it('matches multiple spaces', function () { + $result = EloquentRegex::builder()->pattern()->space('2,5')->toRegex(); + expect($result)->toBe('( ){2,5}'); +}); + +it('matches one or more backslashes', function () { + $result = EloquentRegex::start("\\\\\\")->backslash('+')->check(); + expect($result)->toBe(true); +}); + +it('matches zero or more forward slashes', function () { + $result = EloquentRegex::builder()->start()->forwardSlash('*')->toRegex(); + expect($result)->toBe('(\/)*'); +}); + +it('matches exactly 4 underscores', function () { + $result = EloquentRegex::builder()->start()->underscore('4')->toRegex(); + expect($result)->toBe('(_){4}'); +}); + +it('matches one or more pipes', function () { + $result = EloquentRegex::builder()->start()->pipe('+')->toRegex(); + expect($result)->toBe('(\|)+'); +}); + +it('matches a specific number of character sets', function () { + $regex = EloquentRegex::builder()->start() + ->charSet(function ($pattern) { + $pattern->period()->colon(); + }, '3')->toRegex(); + + expect($regex)->toBe('([\.\:]){3}'); +}); + +it('matches a specific number of negative character sets', function () { + $regex = EloquentRegex::builder()->start() + ->negativeCharSet(function ($pattern) { + // "digits" and similar classes adds quantifier+ automaticaly + // Inside set "+" is parsed as symbol, instead of quantifier + // So, inside charSet and negativeCharSet method, you should + // pass 0 as first argument to do not apply quantifier here + $pattern->digits(0); + }, '2,4')->toRegex(); + + expect($regex)->toBe('([^\d]){2,4}'); +}); + +it('applies quantifier to capturing groups correctly', function () { + $regex = EloquentRegex::builder()->start() + ->group(function ($pattern) { + $pattern->text(); + }, '+')->toRegex(); + + expect($regex)->toBe('([a-zA-Z]+)+'); +}); + +it('applies quantifier to non-capturing groups correctly', function () { + $regex = EloquentRegex::builder()->start() + ->nonCapturingGroup(function ($pattern) { + $pattern->digits(); + }, '*')->toRegex(); + + expect($regex)->toBe('((?:\d+))*'); +}); + +it('uses quantifier with alternation patterns correctly', function () { + $regex = EloquentRegex::builder()->start() + ->group(function ($pattern) { + $pattern->text()->orPattern(function ($pattern) { + $pattern->digits(); + }, "?"); + })->toRegex(); + + expect($regex)->toBe('([a-zA-Z]+|(\d+)?)'); +}); diff --git a/tests/Unit/Patterns/BuilderPatternTest.php b/tests/Unit/Patterns/BuilderPatternTest.php index a3b701b..35ad574 100644 --- a/tests/Unit/Patterns/BuilderPatternTest.php +++ b/tests/Unit/Patterns/BuilderPatternTest.php @@ -223,14 +223,14 @@ $builder = new BuilderPattern(); $builder->doubleQuote()->exact('quoted', true, '*')->doubleQuote(); - $expectedPattern = '/"quoted*"/'; - // $expectedPattern = '/"(quoted)*"/'; // @todo Should return this + // $expectedPattern = '/"quoted*"/'; + $expectedPattern = '/"(quoted)*"/'; // @todo Should return this $regex = $builder->getMatchesValidationPattern(); expect($regex)->toBe($expectedPattern); expect(preg_match($regex, '"quoted"'))->toBe(1); - // expect(preg_match($regex, '"quotedquotedquoted"'))->toBe(1); // @todo Should be true - expect(preg_match($regex, '"quote"'))->toBe(1); // Should match as * allows for zero or more + expect(preg_match($regex, '"quotedquotedquoted"'))->toBe(1); // @todo Should be true + expect(preg_match($regex, '""'))->toBe(1); // Should match as * allows for zero or more expect(preg_match($regex, 'quoted'))->toBe(0); // Should not match without quotes }); From 19a036a85cc27194a8cbf7c50bf808f8b7d56cf9 Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 18:18:38 +0400 Subject: [PATCH 04/10] first part of docs ready --- README.md | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 122 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1e5139f..b64a24a 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,122 @@ Eloquent Regex brings the simplicity and elegance to regular expressions. Designed for Laravel developers, this package offers a fluent, intuitive interface for building and executing regex patterns in your PHP applications. -#### Adding new options +# Overview -All available option classes and option names are hardcoded in `src\OptionsMapper.php`. Refer to it, if you want add new or disable existing one. +### Dreaming of a world where regex doesn't feel like rocket science? 😄🚀 -Think about options as an extra assertions you add to the pattern. To keep it simple, all options (so the option methods too) should have only 1 argument. +Regular expressions (regex) are powerful, no doubt. They're the Swiss Army knife for string manipulation and validation. But let's be honest, they can also be a bit of a headache. The syntax is dense, and a tiny mistake can throw everything off. It's like they're designed to be as intimidating as possible, especially when you're just trying to validate an email address! -#### Adding new patterns +Enter **EloquentRegex**. Our goal is to make working with regex in Laravel not just bearable, but actually enjoyable. Yes, you heard that right—**enjoyable**! -All available redy-to-use pattern classes are hardcoded in `src\Builder.php`. Refer to it, if you want add new or disable existing one. +EloquentRegex is a PHP/Laravel package that offers a fluent, intuitive interface for constructing and executing regular expressions. Whether you need to validate user input, parse text, or extract specific information from strings, EloquentRegex makes it simple and straightforward. For example: + +```php +$isValid = EloquentRegex::source('test@example.com')->email()->check(); +``` + +## Key Features + +- **Ready-to-Use Patterns**: Common patterns like emails, URLs, and IP addresses are pre-defined and ready to go. Just a few keystrokes and you're validating. +- **Custom Patterns Made Easy**: Build your own regex patterns with an easy-to-use, fluent interface. Say hello to readable regex! +- **Options and Filters**: Tailor your regex operations with options and filters for precision matching. It's like having a regex wizard at your fingertips. +- **Laravel Integration**: Seamlessly integrates with your Laravel projects, leveraging Laravel's elegant syntax and features. + +## Getting Started + +Simply install the package via Composer, and you're ready to take the pain out of regex in your PHP/Laravel applications. Follow our quick start guide below to dive in. + +```bash +composer require maestroerror/eloquent-regex +``` + +Later, Here will be added our quick start guide. + +Remember, regex doesn't have to be a source of frustration. With EloquentRegex, you're on your way to becoming a regex master, all while writing cleaner, more maintainable Laravel code. + +# Basic Usage + +EloquentRegex simplifies regular expressions in Laravel, making it easy to validate data, search text, and extract information. This section introduces the basic usage of EloquentRegex, including leveraging ready-to-use patterns and creating custom patterns. + +First of all, you need to include EloquentRegex class. + +```php +use Maestroerror\EloquentRegex\EloquentRegex; +``` + +Recomended for **Laravel:** + +```php +use Maestroerror\EloquentRegex\Facades\EloquentRegex; +``` + +Usage structure is very similar to Laravel's Eloquent ORM, check this out: + +``` +[Initiator][Pattern][?Optional][Action] +``` + +Let's break it down: + +- _Initiator_ sets the target string + +```php +EloquentRegex::source($yourString); +``` + +- _Pattern_ Could be method for one of the ready-to-use patterns or your custom pattern (we will talk about custom pattern later). Let's keep the example simple and add url pattern: + +```php +EloquentRegex::source($yourString)->url(); +``` + +_Note: **Optional** methods mostly are the expression flags, we will talk about them in next sections_ + +- _Action_ are execution methods, check the example: + +```php +// get() will return array/collection of URLs if any found in $yourString +EloquentRegex::source($yourString)->url()->get(); + +// check() will return true if $yourString exactly matches the pattern +// In this case, if $yourString is URL, false otherwise +EloquentRegex::source($yourString)->url()->check(); + +// checkString() will return true if $yourString contains any matches of the pattern +// In this case, if $yourString contains minimum 1 URL, false otherwise +EloquentRegex::source($yourString)->url()->checkString(); + +// count() will return count of matches, in this case, amount of URLs in $yourString +EloquentRegex::source($yourString)->url()->count(); + +// toRegex() will return string - raw regex pattern (without options applied) +EloquentRegex::source($yourString)->url()->toRegex(); +``` + +## Ready-to-Use Patterns + +EloquentRegex comes with a set of predefined patterns for common validation/extraction tasks. These patterns are designed to be straightforward and easy to use, requiring minimal effort to implement. + +We have different ways to apply options, but the most common and easy way is to pass them as arguments. Note that all arguments are optional. + +Here you can check all available methods of ready-to-use patterns and their arguments: + +- `textOrNumbers(int $minLength, int $maxLength, int $minUppercase, int $minLowercase, int $minNumbers, int $maxNumbers)` +- `email(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` - $onlyDomains & $onlyExtensions array or string separated by comma `"example.org,example.com"` +- `url(array|string $onlyProtocol)` +- `domainName(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` - $onlyDomains & $onlyExtensions array or string separated by comma `"org,com"` +- `date()` +- `time()` +- `ipAddress()` +- `ipv6Address()` +- `creditCardNumber(string $cardTypes)` - $cardTypes string separated by comma `"visa, amex"` +- `phone(string $countryCode)` - $countryCode should passed without "+" sign: `phone("1")`, `phone("995")` +- `username(int $maxLength)` +- `password(int $minLength, int $minUppercase, int $minNumbers, int $minSpecialChars)` +- `htmlTag(array|string $restrictTags, array|string $onlyTags)` - $restrictTags & $onlyTags array or string separated by comma `"script, style"`. It isn't recomended to use both option in simultaneously +- `currency(int $minDigits, int $maxDigits, array|string $specificCurrencies)`- $specificCurrencies array of currency symbols or string separated by comma `"$, ₾"` +- `filePath(int $isDirectory, bool $isFile, bool $fileExists,string $pathType)` - $pathType allowed values: `absolute` & `relative` +- `filePathWin(int $isDirectory, bool $isFile, bool $fileExists)` #### Quantifiers @@ -55,6 +162,8 @@ Examples: `->exact("hello world", false, "1+")` - Make Tests for quantifiers (add grouping) ✔️ - Make quantifiers available for special chars ✔️ +- Return collection on get method if laravel is available. +- Create quick start guide and add in Docs. ##### Coming next @@ -65,3 +174,11 @@ Examples: `->exact("hello world", false, "1+")` - Make patterns controllable from config or provider (?) - I should be able to make new pattern using BuilderPattern - I should be able to make add custom pattern to the existing one using BuilderPattern + +``` + +``` + +``` + +``` From 6694fef25faba99dd4e58f891264d9fb8ba09a6f Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 18:27:11 +0400 Subject: [PATCH 05/10] fixes in Docs --- README.md | 98 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b64a24a..7fb2492 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Eloquent Regex brings the simplicity and elegance to regular expressions. Design # Overview -### Dreaming of a world where regex doesn't feel like rocket science? 😄🚀 +### Dreaming of a world where regex doesn't feel like a rocket science? 😄🚀 Regular expressions (regex) are powerful, no doubt. They're the Swiss Army knife for string manipulation and validation. But let's be honest, they can also be a bit of a headache. The syntax is dense, and a tiny mistake can throw everything off. It's like they're designed to be as intimidating as possible, especially when you're just trying to validate an email address! @@ -31,7 +31,7 @@ Simply install the package via Composer, and you're ready to take the pain out o composer require maestroerror/eloquent-regex ``` -Later, Here will be added our quick start guide. +Later, here will be added our quick start guide. Remember, regex doesn't have to be a source of frustration. With EloquentRegex, you're on your way to becoming a regex master, all while writing cleaner, more maintainable Laravel code. @@ -45,7 +45,7 @@ First of all, you need to include EloquentRegex class. use Maestroerror\EloquentRegex\EloquentRegex; ``` -Recomended for **Laravel:** +**Recomended for Laravel:** ```php use Maestroerror\EloquentRegex\Facades\EloquentRegex; @@ -59,21 +59,21 @@ Usage structure is very similar to Laravel's Eloquent ORM, check this out: Let's break it down: -- _Initiator_ sets the target string +- **_Initiator_** sets the target string ```php EloquentRegex::source($yourString); ``` -- _Pattern_ Could be method for one of the ready-to-use patterns or your custom pattern (we will talk about custom pattern later). Let's keep the example simple and add url pattern: +- **_Pattern_** Could be method for one of the ready-to-use patterns or your custom pattern (we will talk about custom pattern later). Let's keep the example simple and add url pattern: ```php EloquentRegex::source($yourString)->url(); ``` -_Note: **Optional** methods mostly are the expression flags, we will talk about them in next sections_ +_Note: **?Optional** methods mostly are the expression flags, we will talk about them in next sections_ -- _Action_ are execution methods, check the example: +- **_Action_** is the execution method, check the example: ```php // get() will return array/collection of URLs if any found in $yourString @@ -102,22 +102,74 @@ We have different ways to apply options, but the most common and easy way is to Here you can check all available methods of ready-to-use patterns and their arguments: -- `textOrNumbers(int $minLength, int $maxLength, int $minUppercase, int $minLowercase, int $minNumbers, int $maxNumbers)` -- `email(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` - $onlyDomains & $onlyExtensions array or string separated by comma `"example.org,example.com"` -- `url(array|string $onlyProtocol)` -- `domainName(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` - $onlyDomains & $onlyExtensions array or string separated by comma `"org,com"` -- `date()` -- `time()` -- `ipAddress()` -- `ipv6Address()` -- `creditCardNumber(string $cardTypes)` - $cardTypes string separated by comma `"visa, amex"` -- `phone(string $countryCode)` - $countryCode should passed without "+" sign: `phone("1")`, `phone("995")` -- `username(int $maxLength)` -- `password(int $minLength, int $minUppercase, int $minNumbers, int $minSpecialChars)` -- `htmlTag(array|string $restrictTags, array|string $onlyTags)` - $restrictTags & $onlyTags array or string separated by comma `"script, style"`. It isn't recomended to use both option in simultaneously -- `currency(int $minDigits, int $maxDigits, array|string $specificCurrencies)`- $specificCurrencies array of currency symbols or string separated by comma `"$, ₾"` -- `filePath(int $isDirectory, bool $isFile, bool $fileExists,string $pathType)` - $pathType allowed values: `absolute` & `relative` -- `filePathWin(int $isDirectory, bool $isFile, bool $fileExists)` +```php +textOrNumbers(int $minLength, int $maxLength, int $minUppercase, int $minLowercase, int $minNumbers, int $maxNumbers) +``` + +```php +// $onlyDomains & $onlyExtensions array or string separated by comma `"example.org,example.com"` +email(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` +``` + +````php +url(array|string $onlyProtocol)` +```php +// $onlyDomains & $onlyExtensions array or string separated by comma "org,com" +domainName(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` +```` + +```php +date() +``` + +```php +time() +``` + +```php +ipAddress() +``` + +```php +ipv6Address() +``` + +```php +// $cardTypes string separated by comma "visa, amex" +creditCardNumber(string $cardTypes) +``` + +```php +// $countryCode should passed without "+" sign: phone("1"), phone("995") +phone(string $countryCode) +``` + +```php +username(int $maxLength) +``` + +```php +password(int $minLength, int $minUppercase, int $minNumbers, int $minSpecialChars) +``` + +```php +// $restrictTags & $onlyTags array or string separated by comma `"script, style"`. It isn't recomended to use both option in simultaneously +htmlTag(array|string $restrictTags, array|string $onlyTags) +``` + +```php +// $specificCurrencies array of currency symbols or string separated by comma "$, ₾" +currency(int $minDigits, int $maxDigits, array|string $specificCurrencies) +``` + +```php +// $pathType allowed values: "absolute" & "relative" +filePath(int $isDirectory, bool $isFile, bool $fileExists,string $pathType) - +``` + +```php +filePathWin(int $isDirectory, bool $isFile, bool $fileExists) +``` #### Quantifiers From 915fac60a91a897669b150d03945cc8428c616af Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 18:42:48 +0400 Subject: [PATCH 06/10] Custom pattern section --- README.md | 49 +++++- src/Patterns/BuilderPattern.php | 145 +--------------- .../BuilderPatternTraits/GroupsTrait.php | 156 ++++++++++++++++++ 3 files changed, 204 insertions(+), 146 deletions(-) create mode 100644 src/Traits/BuilderPatternTraits/GroupsTrait.php diff --git a/README.md b/README.md index 7fb2492..076a74c 100644 --- a/README.md +++ b/README.md @@ -111,12 +111,14 @@ textOrNumbers(int $minLength, int $maxLength, int $minUppercase, int $minLowerca email(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` ``` -````php +```php url(array|string $onlyProtocol)` +``` + ```php // $onlyDomains & $onlyExtensions array or string separated by comma "org,com" domainName(int $maxLength, array|string $onlyDomains, array|string $onlyExtensions)` -```` +``` ```php date() @@ -153,7 +155,9 @@ password(int $minLength, int $minUppercase, int $minNumbers, int $minSpecialChar ``` ```php -// $restrictTags & $onlyTags array or string separated by comma `"script, style"`. It isn't recomended to use both option in simultaneously +// $restrictTags & $onlyTags array or string +// separated by comma `"script, style"`. +// It isn't recomended to use both option simultaneously htmlTag(array|string $restrictTags, array|string $onlyTags) ``` @@ -171,6 +175,45 @@ filePath(int $isDirectory, bool $isFile, bool $fileExists,string $pathType) - filePathWin(int $isDirectory, bool $isFile, bool $fileExists) ``` +Didn't it cover all your needs? Let's take a look to the custom patterns section. + +## Custom Patterns + +For scenarios where predefined patterns do not suffice, EloquentRegex allows you to define custom patterns using the start or customPattern methods as initiator: + +```php +EloquentRegex::start($yourString); +// Or +EloquentRegex::customPattern($yourString); +``` + +_Note: They does the exaclty same thing, you can use your favorite one_ + +### Creating a Custom Pattern + +You can start building a custom pattern to match a specific string format, such as a custom ID format that starts with letters followed by digits: + +```php +$result = EloquentRegex::start('ID123456') + ->literal('ID') + ->digitsRange(1, 10) + ->check(); + +if ($result) { + echo "The string matches the custom ID format!"; +} else { + echo "The string does not match the custom ID format."; +} + +``` + +Custom pattern builder supports a wide range of character classes and all special chars. Also, `literal` or `exact` method could be used to match exact string you need, or `char` method could be used to match exact character. The full list of pattern builder methods is comming soon. Before that, you can check this files out: + +- [Character Classes](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/CharacterClassesTrait.php) +- [Special characters](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php) +- [Groups](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/GroupsTrait.php) +- [Anchors](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/AnchorsTrait.php) + #### Quantifiers Available values for quantifiers as argument: diff --git a/src/Patterns/BuilderPattern.php b/src/Patterns/BuilderPattern.php index 38b2e9d..db4b462 100644 --- a/src/Patterns/BuilderPattern.php +++ b/src/Patterns/BuilderPattern.php @@ -7,13 +7,14 @@ use Maestroerror\EloquentRegex\Traits\BuilderPatternTraits\CharacterClassesTrait; use Maestroerror\EloquentRegex\Traits\BuilderPatternTraits\SpecificCharsTrait; use Maestroerror\EloquentRegex\Traits\BuilderPatternTraits\AnchorsTrait; +use Maestroerror\EloquentRegex\Traits\BuilderPatternTraits\GroupsTrait; use Maestroerror\EloquentRegex\Builder; class BuilderPattern extends BasePattern { // BuilderPattern doesn't need the "execute" method (src\Traits\Pattern.php) - use CharacterClassesTrait, SpecificCharsTrait, AnchorsTrait; + use CharacterClassesTrait, SpecificCharsTrait, AnchorsTrait, GroupsTrait; /** * @var array Array of options for the pattern. @@ -174,147 +175,5 @@ public function lazy(): self { return $this; } - /** - * Adds a new set of characters. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. - * @return self - */ - public function charSet(callable $callback, ?string $q = null): self { - $subPattern = new self(); - $callback($subPattern); - $p = '[' . $subPattern->getPattern() . ']'; - $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; - return $this; - } - - /** - * Adds a new set of denied characters. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. - * @return self - */ - public function negativeCharSet(callable $callback, ?string $q = null): self { - $subPattern = new self(); - $callback($subPattern); - $p = '[^' . $subPattern->getPattern() . ']'; - $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; - return $this; - } - - /** - * Adds a new grouped subpattern. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. - * @return self - */ - public function group(callable $callback, ?string $q = null): self { - $subPattern = new self(); - $callback($subPattern); - $p = $subPattern->getPattern(); - $this->pattern .= $q ? $this->applyQuantifier($p, $q) : '(' . $p . ')'; - return $this; - } - - /** - * Adds a new non-capturing grouped subpattern. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. - * @return self - */ - public function nonCapturingGroup(callable $callback, ?string $q = null): self { - $subPattern = new self(); - $callback($subPattern); - $p = '(?:' . $subPattern->getPattern() . ')'; - $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; - return $this; - } - /** - * Adds an alternation pattern. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the alternation. - * @return self - */ - public function orPattern(callable $callback, ?string $q = null): self { - $builder = new self(); - $callback($builder); - $p = $builder->getPattern(); - $this->pattern .= $q ? '|' . $this->applyQuantifier($p, $q) : '|' . $p; - return $this; - } - - /** - * Adds a positive lookahead assertion. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. - * @return self - */ - public function lookAhead(callable $callback): self { - $builder = new self(); - $callback($builder); - $this->pattern .= '(?=' . $builder->getPattern() . ')'; - return $this; - } - - /** - * Adds a positive lookbehind assertion. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. - * @return self - */ - public function lookBehind(callable $callback): self { - $builder = new self(); - $callback($builder); - $this->pattern .= '(?<=' . $builder->getPattern() . ')'; - return $this; - } - - /** - * Adds a negative lookahead assertion. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. - * @return self - */ - public function negativeLookAhead(callable $callback): self { - $builder = new self(); - $callback($builder); - $this->pattern .= '(?!' . $builder->pattern . ')'; - return $this; - } - - /** - * Adds a negative lookbehind assertion. - * - * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. - * @return self - */ - public function negativeLookBehind(callable $callback): self { - $builder = new self(); - $callback($builder); - $this->pattern .= '(?pattern . ')'; - return $this; - } - - /** - * Adds a raw regex string to the pattern. - * - * @param string $regex The raw regex string to add. - * @return self - */ - public function addRawRegex(string $regex): self { - $this->pattern .= $regex; - return $this; - } - - /** - * Wraps a given regex string in a non-capturing group and adds it to the pattern. - * - * @param string $regex The regex string to wrap and add. - * @return self - */ - public function addRawNonCapturingGroup(string $regex): self { - $this->pattern .= '(?:' . $regex . ')'; - return $this; - } } \ No newline at end of file diff --git a/src/Traits/BuilderPatternTraits/GroupsTrait.php b/src/Traits/BuilderPatternTraits/GroupsTrait.php new file mode 100644 index 0000000..a13e270 --- /dev/null +++ b/src/Traits/BuilderPatternTraits/GroupsTrait.php @@ -0,0 +1,156 @@ +getPattern() . ']'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; + return $this; + } + + /** + * Adds a new set of denied characters. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. + * @param ?string $q a Quantifier + * @return self + */ + public function negativeCharSet(callable $callback, ?string $q = null): self { + $subPattern = new self(); + $callback($subPattern); + $p = '[^' . $subPattern->getPattern() . ']'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; + return $this; + } + + /** + * Adds a new grouped subpattern. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. + * @param ?string $q a Quantifier + * @return self + */ + public function group(callable $callback, ?string $q = null): self { + $subPattern = new self(); + $callback($subPattern); + $p = $subPattern->getPattern(); + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : '(' . $p . ')'; + return $this; + } + + /** + * Adds a new non-capturing grouped subpattern. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the subpattern. + * @param ?string $q a Quantifier + * @return self + */ + public function nonCapturingGroup(callable $callback, ?string $q = null): self { + $subPattern = new self(); + $callback($subPattern); + $p = '(?:' . $subPattern->getPattern() . ')'; + $this->pattern .= $q ? $this->applyQuantifier($p, $q) : $p; + return $this; + } + + /** + * Adds an alternation pattern. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the alternation. + * @param ?string $q a Quantifier + * @return self + */ + public function orPattern(callable $callback, ?string $q = null): self { + $builder = new self(); + $callback($builder); + $p = $builder->getPattern(); + $this->pattern .= $q ? '|' . $this->applyQuantifier($p, $q) : '|' . $p; + return $this; + } + + /** + * Adds a positive lookahead assertion. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. + * @return self + */ + public function lookAhead(callable $callback): self { + $builder = new self(); + $callback($builder); + $this->pattern .= '(?=' . $builder->getPattern() . ')'; + return $this; + } + + /** + * Adds a positive lookbehind assertion. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. + * @return self + */ + public function lookBehind(callable $callback): self { + $builder = new self(); + $callback($builder); + $this->pattern .= '(?<=' . $builder->getPattern() . ')'; + return $this; + } + + /** + * Adds a negative lookahead assertion. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. + * @return self + */ + public function negativeLookAhead(callable $callback): self { + $builder = new self(); + $callback($builder); + $this->pattern .= '(?!' . $builder->pattern . ')'; + return $this; + } + + /** + * Adds a negative lookbehind assertion. + * + * @param callable $callback A callback that receives a BuilderPattern instance to define the assertion. + * @return self + */ + public function negativeLookBehind(callable $callback): self { + $builder = new self(); + $callback($builder); + $this->pattern .= '(?pattern . ')'; + return $this; + } + + /** + * Adds a raw regex string to the pattern. + * + * @param string $regex The raw regex string to add. + * @return self + */ + public function addRawRegex(string $regex): self { + $this->pattern .= $regex; + return $this; + } + + /** + * Wraps a given regex string in a non-capturing group and adds it to the pattern. + * + * @param string $regex The regex string to wrap and add. + * @return self + */ + public function addRawNonCapturingGroup(string $regex): self { + $this->pattern .= '(?:' . $regex . ')'; + return $this; + } + +} From cf6f77827a1db40c7dbb01316fd52abbe78565d4 Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 19:11:42 +0400 Subject: [PATCH 07/10] Quantifiers section done --- README.md | 141 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 076a74c..0712967 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,8 @@ if ($result) { ``` +_Note: You can use `EloquentRegex::builder()->pattern()` if you need just build a regex without source string_ + Custom pattern builder supports a wide range of character classes and all special chars. Also, `literal` or `exact` method could be used to match exact string you need, or `char` method could be used to match exact character. The full list of pattern builder methods is comming soon. Before that, you can check this files out: - [Character Classes](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/CharacterClassesTrait.php) @@ -214,51 +216,122 @@ Custom pattern builder supports a wide range of character classes and all specia - [Groups](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/GroupsTrait.php) - [Anchors](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/AnchorsTrait.php) -#### Quantifiers +## Applying Quantifiers + +Quantifiers in regular expressions are symbols or sets of symbols that specify how many instances of a character, group, or character class must be present in the input for a match to be found. EloquentRegex enhances the way quantifiers are used, making it simpler and more intuitive to define the frequency of pattern occurrences. + +### Optional Elements + +To make an element optional, use '?'. This matches zero or one occurrence of the preceding element. + +```php +// Matches a string that may or may not contain a dash +$result = EloquentRegex::start($yourString)->exact("123")->dash('?')->exact("456")->check(); +// Result would be true in both cases of $yourString: "123456" & "123-456" +``` + +### Specifying a Range + +For specifying a range of occurrences, use a string with two numbers separated by a comma '2,5'. This matches the preceding element at least and at most the specified times. + +```php +// Matches a string with 2 to 5 spaces +$result = EloquentRegex::start($yourString)->text()->space('2,5')->digits()->check(); +// Result: the "someText 234" would return true, the "someText 234" false +``` + +### One or More + +To match one or more occurrences of an element, use '+', '1+', '1>' or 'oneOrMore'. This ensures the element appears at least once. + +```php +// Matches strings with one or more backslashes +$result = EloquentRegex::start("\\\\")->backslash('1+')->check(); +// Result: true (if one or more backslashes are found) +``` + +### Zero or More + +The '\*' quantifier matches zero or more occurrences of the preceding element. + +```php +// Matches strings with zero or more forward slashes +$result = EloquentRegex::start($yourString)->alphanumeric()->dot('0+')->check(); +// Result would be true in both cases of $yourString: "test258..." & "test" +``` + +### Exact Number -Available values for quantifiers as argument: +To match an exact number of occurrences, directly specify the number. -- zeroOrMore = `"zeroOrMore"`, `"0>"`, `"0+"` -- oneOrMore = `"oneOrMore"`, `"1>"`, `"1+"` -- optional = `"optional"`, `"?"`, `"|"` +```php +// Matches strings with exactly 2 underscores +$result = EloquentRegex::start($yourString)->digits()->underscore('2')->digits()->check(); +// Result would be true in cases $yourString: "1235__158", but "1235___158" and "1235_158" will be false + +``` + +### Custom Character Sets and groups + +You can apply quantifiers to custom character sets and groups as second argument after the callback, matching a specific number of occurrences. + +```php +// Matches strings with exactly 3 periods or colons +$regex = EloquentRegex::builder()->start() + ->charSet(function ($pattern) { + $pattern->period()->colon(); + }, '3')->toRegex(); +// Result: ([\.\:]){3} +``` + +### Quantifier values + +In [Special characters](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php) +and +[Groups](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/GroupsTrait.php)Available +nearly all methods allowing quantifiers with values: + +- Zero or More = `"zeroOrMore"`, `"0>"`, `"0+"`, `"*"` +- One or More = `"oneOrMore"`, `"1>"`, `"1+"`, `"+"` +- Optional (Zero or One) = `"optional"`, `"?"`, `"|"` +- exact amount = `2`, `"5"` +- range = `"{0,5}"` + +Example: `->exact("hello world", false, "1+")` -Examples: `->exact("hello world", false, "1+")` +But +[Character Classes](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/CharacterClassesTrait.php) +have different approach, lets take `digits` as example: + +```php +// By defualt it is set as One or More +EloquentRegex::start($yourString)->digits(); + +// You can completly remove quantifier by passing 0 as first argument +EloquentRegex::start($yourString)->digits(0); + +// You can specify exact amount of digit by passing int +EloquentRegex::start($yourString)->digits(5); + +// You can specify range of digits by adding "Range" to the method +EloquentRegex::start($yourString)->digitsRange(1, 5); // Matches from 1 to 5 digits +``` ##### To Do -- Add needed options for new patterns: +- Add options for new patterns: - usernameLength: Set minimum and maximum length for the username part of the email. - dateFormat, timeFormat: Specify the format of date and time (e.g., MM-DD-YYYY, HH:MM). - Consider to register Patterns like options using key (name) => value (class) pairs (check performance) ✔️ (_No significant change before 50+ patterns_) -- Extend BuilderPattern, try to add methods: - - - group(callable $callback): Creates a grouped subpattern.✔️ - - nonCapturingGroup(callable $callback): Creates a non-capturing group.✔️ - - orPattern(): Alternation, allowing for multiple possibilities.✔️ - - lookAhead(callable $callback): Positive lookahead assertion.✔️ - - lookBehind(callable $callback): Positive lookbehind assertion.✔️ - - negativeLookAhead(callable $callback): Negative lookahead assertion.✔️ - - negativeLookBehind(callable $callback): Negative lookbehind assertion.✔️ - - Raw regex methods for advanced users.✔️ - - BuilderPattern should be able to reproduce patterns used in HSA✔️ - -- Add benchmarks and tests for search against large data ✔️ -- Add Feature Tests for BuilderPattern ✔️ -- Remove need for "end" method in BuilderPattern ✔️ -- Add Dockblocs and comments for new methods ✔️ - -- Add facade for Laravel ✔️ -- Wrap Builder in class for static start ✔️ - - "string" and "source" for builder start ✔️ - - "start" and "pattern" for builderPattern start ✔️ - Write documentation (add credit for https://regexr.com/ and ChatGPT) -- Add automated tests on PR creation or on marging to main branch ✔️ - -- Make Tests for quantifiers (add grouping) ✔️ -- Make quantifiers available for special chars ✔️ - Return collection on get method if laravel is available. - Create quick start guide and add in Docs. +- Add advanced usage section in Docs: + - Options and Assertions: Detailed explanation of options, how to apply them, and their effects on patterns. + - Filters in Extraction: Using options as filters during extraction and the types of filters available. + - Regex Flags: Guide on applying regex flags to patterns for specialized matching behavior. + - Grouping and Capturing: How to use groups (capturing and non-capturing) and apply quantifiers to them. ##### Coming next @@ -273,7 +346,3 @@ Examples: `->exact("hello world", false, "1+")` ``` ``` - -``` - -``` From 8dd3d148316ddb66d4a585d687f7a385c04f667e Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 19:14:17 +0400 Subject: [PATCH 08/10] Updated ToDo --- README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0712967..db65336 100644 --- a/README.md +++ b/README.md @@ -323,15 +323,22 @@ EloquentRegex::start($yourString)->digitsRange(1, 5); // Matches from 1 to 5 dig - usernameLength: Set minimum and maximum length for the username part of the email. - dateFormat, timeFormat: Specify the format of date and time (e.g., MM-DD-YYYY, HH:MM). - Consider to register Patterns like options using key (name) => value (class) pairs (check performance) ✔️ (_No significant change before 50+ patterns_) +- Return collection on get method if laravel is available. - Write documentation (add credit for https://regexr.com/ and ChatGPT) -- Return collection on get method if laravel is available. -- Create quick start guide and add in Docs. -- Add advanced usage section in Docs: - - Options and Assertions: Detailed explanation of options, how to apply them, and their effects on patterns. - - Filters in Extraction: Using options as filters during extraction and the types of filters available. - - Regex Flags: Guide on applying regex flags to patterns for specialized matching behavior. - - Grouping and Capturing: How to use groups (capturing and non-capturing) and apply quantifiers to them. + - Create quick start guide and add in Docs. + - Add advanced usage section in Docs: + - Options and Assertions: Detailed explanation of options, how to apply them, and their effects on patterns. + - Filters in Extraction: Using options as filters during extraction and the types of filters available. + - Regex Flags: Guide on applying regex flags to patterns for specialized matching behavior. + - Grouping and Capturing: How to use groups (capturing and non-capturing) and apply quantifiers to them. + - Add section in docs for "lazy" method + - Add in docs sections: + - Testing and Debugging + - Credits + - Contributing + - FAQs + - Creating new patterns ##### Coming next From 6ab00fbbdb11477b054353592fa6b67d180fa746 Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 19:29:01 +0400 Subject: [PATCH 09/10] Update docs --- README.md | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index db65336..78c8447 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,29 @@ Eloquent Regex brings the simplicity and elegance to regular expressions. Designed for Laravel developers, this package offers a fluent, intuitive interface for building and executing regex patterns in your PHP applications. +### Table of Contents + +- [Overview](#overview) + - [Key Features](#key-features) + - [Getting Started](#getting-started) +- [Basic Usage](#basic-usage) + - [Including EloquentRegex](#including-eloquentregex) + - [Usage Structure](#usage-structure) + - [Ready-to-Use Patterns](#ready-to-use-patterns) + - [Custom Patterns](#custom-patterns) + - [Creating a Custom Pattern](#creating-a-custom-pattern) + - [Applying Quantifiers](#applying-quantifiers) + - [Optional Elements](#optional-elements) + - [Specifying a Range](#specifying-a-range) + - [One or More](#one-or-more) + - [Zero or More](#zero-or-more) + - [Exact Number](#exact-number) + - [Custom Character Sets and Groups](#custom-character-sets-and-groups) + - [Quantifier Values](#quantifier-values) + # Overview -### Dreaming of a world where regex doesn't feel like a rocket science? 😄🚀 +#### Dreaming of a world where regex doesn't feel like a rocket science? 😄🚀 Regular expressions (regex) are powerful, no doubt. They're the Swiss Army knife for string manipulation and validation. But let's be honest, they can also be a bit of a headache. The syntax is dense, and a tiny mistake can throw everything off. It's like they're designed to be as intimidating as possible, especially when you're just trying to validate an email address! @@ -222,7 +242,7 @@ Quantifiers in regular expressions are symbols or sets of symbols that specify h ### Optional Elements -To make an element optional, use '?'. This matches zero or one occurrence of the preceding element. +To make an element optional, use '?'. This matches zero or one occurrence of the preceding element (`dash` in this example). ```php // Matches a string that may or may not contain a dash @@ -237,7 +257,7 @@ For specifying a range of occurrences, use a string with two numbers separated b ```php // Matches a string with 2 to 5 spaces $result = EloquentRegex::start($yourString)->text()->space('2,5')->digits()->check(); -// Result: the "someText 234" would return true, the "someText 234" false +// Result: the "someText 234" would return true, but the "someText 234" false ``` ### One or More @@ -252,7 +272,7 @@ $result = EloquentRegex::start("\\\\")->backslash('1+')->check(); ### Zero or More -The '\*' quantifier matches zero or more occurrences of the preceding element. +The '0+' quantifier matches zero or more occurrences of the preceding element. ```php // Matches strings with zero or more forward slashes @@ -287,9 +307,7 @@ $regex = EloquentRegex::builder()->start() ### Quantifier values In [Special characters](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/SpecificCharsTrait.php) -and -[Groups](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/GroupsTrait.php)Available -nearly all methods allowing quantifiers with values: +and [Groups](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/GroupsTrait.php) - nearly all methods allowing quantifiers with values: - Zero or More = `"zeroOrMore"`, `"0>"`, `"0+"`, `"*"` - One or More = `"oneOrMore"`, `"1>"`, `"1+"`, `"+"` @@ -297,7 +315,7 @@ nearly all methods allowing quantifiers with values: - exact amount = `2`, `"5"` - range = `"{0,5}"` -Example: `->exact("hello world", false, "1+")` +Example: `->literal("hello world", false, "1+")` But [Character Classes](https://github.com/MaestroError/eloquent-regex/blob/documentation-and-examples/src/Traits/BuilderPatternTraits/CharacterClassesTrait.php) @@ -317,6 +335,8 @@ EloquentRegex::start($yourString)->digits(5); EloquentRegex::start($yourString)->digitsRange(1, 5); // Matches from 1 to 5 digits ``` +--- + ##### To Do - Add options for new patterns: @@ -333,7 +353,7 @@ EloquentRegex::start($yourString)->digitsRange(1, 5); // Matches from 1 to 5 dig - Regex Flags: Guide on applying regex flags to patterns for specialized matching behavior. - Grouping and Capturing: How to use groups (capturing and non-capturing) and apply quantifiers to them. - Add section in docs for "lazy" method - - Add in docs sections: + - Add sections: - Testing and Debugging - Credits - Contributing @@ -349,7 +369,3 @@ EloquentRegex::start($yourString)->digitsRange(1, 5); // Matches from 1 to 5 dig - Make patterns controllable from config or provider (?) - I should be able to make new pattern using BuilderPattern - I should be able to make add custom pattern to the existing one using BuilderPattern - -``` - -``` From 7edae17b8a3c22a26828545a9a492ceaacd4370e Mon Sep 17 00:00:00 2001 From: maestroerror Date: Wed, 28 Feb 2024 19:40:42 +0400 Subject: [PATCH 10/10] Update docs --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 78c8447..b8e86e1 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ Eloquent Regex brings the simplicity and elegance to regular expressions. Design - [Ready-to-Use Patterns](#ready-to-use-patterns) - [Custom Patterns](#custom-patterns) - [Creating a Custom Pattern](#creating-a-custom-pattern) - - [Applying Quantifiers](#applying-quantifiers) - - [Optional Elements](#optional-elements) - - [Specifying a Range](#specifying-a-range) - - [One or More](#one-or-more) - - [Zero or More](#zero-or-more) - - [Exact Number](#exact-number) - - [Custom Character Sets and Groups](#custom-character-sets-and-groups) - - [Quantifier Values](#quantifier-values) + - [Applying Quantifiers](#applying-quantifiers) + - [Optional Elements](#optional-elements) + - [Specifying a Range](#specifying-a-range) + - [One or More](#one-or-more) + - [Zero or More](#zero-or-more) + - [Exact Number](#exact-number) + - [Custom Character Sets and Groups](#custom-character-sets-and-groups) + - [Quantifier Values](#quantifier-values) # Overview