From b9b56adfd3ef5b36b4291266bec57e746b319316 Mon Sep 17 00:00:00 2001 From: Marty Friedel <1491079+martyf@users.noreply.github.com> Date: Thu, 29 Feb 2024 01:23:04 +1100 Subject: [PATCH] [4.x] Support for validation Rule objects (#9332) --- src/Fields/ClassRuleParser.php | 44 +++++++++++++++++ src/Fields/Validator.php | 6 +++ tests/Fields/ClassRuleParserTest.php | 71 ++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/Fields/ClassRuleParser.php create mode 100644 tests/Fields/ClassRuleParserTest.php diff --git a/src/Fields/ClassRuleParser.php b/src/Fields/ClassRuleParser.php new file mode 100644 index 0000000000..b952369fdd --- /dev/null +++ b/src/Fields/ClassRuleParser.php @@ -0,0 +1,44 @@ +between('(', ')') + ->explode(',') + ->mapWithKeys(function ($arg, $key) { + $arg = trim($arg); + + if (preg_match('/^[a-zA-Z]+: ?/', $arg)) { + [$key, $arg] = explode(':', $arg, 2); + $key = trim($key); + $arg = trim($arg); + } + + if (is_numeric($arg)) { + return [$key => str_contains($arg, '.') ? (float) $arg : (int) $arg]; + } elseif (Str::startsWith($arg, '"') && Str::endsWith($arg, '"')) { + return [$key => (string) Str::of($arg)->trim('"')->replace('\\"', '"')]; + } elseif (Str::startsWith($arg, "'") && Str::endsWith($arg, "'")) { + return [$key => (string) Str::of($arg)->trim("'")->replace("\\'", "'")]; + } + + return [$key => $arg]; + }); + + return [$class, $arguments->all()]; + } +} diff --git a/src/Fields/Validator.php b/src/Fields/Validator.php index b215b6057c..91ce5157df 100644 --- a/src/Fields/Validator.php +++ b/src/Fields/Validator.php @@ -124,6 +124,12 @@ public function attributes() private function parse($rule) { + if (is_string($rule) && Str::startsWith($rule, 'new ')) { + [$class, $arguments] = (new ClassRuleParser)->parse($rule); + + return new $class(...$arguments); + } + if (! is_string($rule) || ! Str::contains($rule, '{') || Str::startsWith($rule, 'regex:') || diff --git a/tests/Fields/ClassRuleParserTest.php b/tests/Fields/ClassRuleParserTest.php new file mode 100644 index 0000000000..18d97578cc --- /dev/null +++ b/tests/Fields/ClassRuleParserTest.php @@ -0,0 +1,71 @@ +parse($input); + + $this->assertSame($expected, $output); + } + + public static function classRuleProvider() + { + return [ + 'just class' => [ + 'new App\MyRule', + ['App\MyRule', []], + ], + 'single quoted string' => [ + "new App\MyRule('foo')", + ['App\MyRule', ['foo']], + ], + 'double quoted string' => [ + 'new App\MyRule("foo")', + ['App\MyRule', ['foo']], + ], + 'multiple arguments' => [ + "new App\MyRule('foo', 123, 'bar')", + ['App\MyRule', ['foo', 123, 'bar']], + ], + 'quote in a single quoted string' => [ + "new App\MyRule('it\'s a me mario')", + ['App\MyRule', ["it's a me mario"]], + ], + 'double quote in a double quoted string' => [ + 'new App\MyRule("stop trying to make \"fetch\" happen")', + ['App\MyRule', ['stop trying to make "fetch" happen']], + ], + 'only named arguments' => [ + 'new App\MyRule(a: "foo", b: 123)', + ['App\MyRule', ['a' => 'foo', 'b' => 123]], + ], + 'only named arguments with no spaces' => [ + 'new App\MyRule(a:"foo", b:123)', + ['App\MyRule', ['a' => 'foo', 'b' => 123]], + ], + 'some named arguments' => [ + 'new App\MyRule("foo", c: 123)', + ['App\MyRule', ['foo', 'c' => 123]], + ], + 'non-named argument with colon' => [ + 'new App\MyRule("foo:bar")', + ['App\MyRule', ['foo:bar']], + ], + 'named argument with colon' => [ + 'new App\MyRule(a: "foo:bar")', + ['App\MyRule', ['a' => 'foo:bar']], + ], + ]; + } +}