Skip to content

Commit

Permalink
[4.x] Support for validation Rule objects (#9332)
Browse files Browse the repository at this point in the history
  • Loading branch information
martyf authored Feb 28, 2024
1 parent 7da46d8 commit b9b56ad
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/Fields/ClassRuleParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Statamic\Fields;

use Statamic\Support\Str;

class ClassRuleParser
{
public function parse(string $rule): array
{
$rule = Str::substr($rule, 4);

if (! str_contains($rule, '(')) {
return [$rule, []];
}

$class = Str::before($rule, '(');

$arguments = Str::of($rule)
->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()];
}
}
6 changes: 6 additions & 0 deletions src/Fields/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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:') ||
Expand Down
71 changes: 71 additions & 0 deletions tests/Fields/ClassRuleParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Tests\Fields;

use Statamic\Fields\ClassRuleParser;
use Tests\TestCase;

class ClassRuleParserTest extends TestCase
{
/**
* @test
*
* @dataProvider classRuleProvider
*/
public function it_parses_class_rules($input, $expected)
{
$output = (new ClassRuleParser)->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']],
],
];
}
}

0 comments on commit b9b56ad

Please sign in to comment.