Passed
Pull Request — master (#222)
by Dmitriy
02:24
created

JsonValidator::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 4
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule\Json;
6
7
use Yiisoft\Validator\Result;
8
use Yiisoft\Validator\Rule\RuleValidatorInterface;
9
use Yiisoft\Validator\ValidationContext;
10
use Yiisoft\Validator\ValidatorInterface;
11
use function is_string;
12
13
/**
14
 * Validates that the value is a valid json.
15
 */
16
final class JsonValidator implements RuleValidatorInterface
17
{
18 9
    public function validate(mixed $value, object $rule, ValidatorInterface $validator, ?ValidationContext $context = null): Result
19
    {
20 9
        $result = new Result();
21
22 9
        if (!$this->isValidJson($value)) {
23 6
            $result->addError($rule->message);
24
        }
25
26 9
        return $result;
27
    }
28
29 9
    private function isValidJson($value): bool
30
    {
31
        // Regular expression is built based on JSON grammar specified at
32
        // https://tools.ietf.org/html/rfc8259
33 9
        $regex = <<<'REGEX'
34
        /
35
        (?(DEFINE)
36
            (?<json>(?>\s*(?&object)\s*|\s*(?&array)\s*))
37
            (?<object>(?>\{\s*(?>(?&member)(?>\s*,\s*(?&member))*)?\s*\}))
38
            (?<member>(?>(?&string)\s*:\s*(?&value)))
39
            (?<array>(?>\[\s*(?>(?&value)(?>\s*,\s*(?&value))*)?\s*\]))
40
            (?<value>(?>)false|null|true|(?&object)|(?&array)|(?&number)|(?&string))
41
            (?<number>(?>-?(?>0|[1-9]\d*)(?>\.\d+)?(?>[eE][-+]?\d+)?))
42
            (?<string>(?>"(?>\\(?>["\\\/bfnrt]|u[a-fA-F0-9]{4})|[^"\\\0-\x1F\x7F]+)*"))
43
        )
44
        \A(?&json)\z
45
        /x
46
        REGEX;
47
48 9
        return is_string($value) && preg_match($regex, $value) === 1;
49
    }
50
}
51