Test Failed
Branch feature/upgrade-code-quality (805160)
by Csaba
01:56
created

SchemaValidator::objectHasSchemaMethod()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.7462

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 12
ccs 3
cts 7
cp 0.4286
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 2.7462
1
<?php
2
namespace Fathomminds\Rest\Schema;
3
4
use Fathomminds\Rest\Schema\TypeValidators\ValidatorFactory;
5
use Fathomminds\Rest\Exceptions\RestException;
6
7
class SchemaValidator
8
{
9
    protected $fields = [];
10
    protected $allowExtraneous = false;
11
    protected $requiredSchemaClass = null;
12
    private $updateMode = false;
13
14 29
    public function __construct($requiredSchemaClass = null)
15
    {
16 29
        $this->requiredSchemaClass = $requiredSchemaClass;
17 29
    }
18
19 1
    public function updateMode($updateMode = null)
20
    {
21 1
        if (is_bool($updateMode)) {
22 1
            $this->updateMode = $updateMode;
23
        }
24 1
        return $this->updateMode;
25
    }
26
27 1
    public function validate($resource)
28
    {
29 1
        $this->validateResourceType($resource);
30 1
        $extraneousCheck = [];
31 1
        if (!$this->allowExtraneous) {
32
            $extraneousCheck = $this->validateExtraneousFields($resource);
33
        }
34 1
        $errors = array_merge(
35 1
            $this->validateRequiredFields($resource),
36 1
            $extraneousCheck,
37 1
            $this->validateFieldTypes($resource)
38
        );
39 1
        if (!empty($errors)) {
40
            throw new RestException(
41
                'Invalid structure',
42
                [
43
                    'schema' => get_class($resource),
44
                    'errors' => $errors,
45
                ]
46
            );
47
        }
48 1
    }
49
50 28
    public function allowExtraneous($value)
51
    {
52 28
        $this->allowExtraneous = $value;
53 28
    }
54
55 1
    private function validateResourceType($resource)
56
    {
57 1
        $this->expectObject($resource);
58 1
        $this->objectHasSchemaMethod($resource);
59 1
        $this->objectIsValidSchemaClass($resource);
60 1
    }
61
62 2
    private function expectObject($resource)
63
    {
64 2
        if (gettype($resource) !== 'object') {
65 1
            throw new RestException(
66 1
                'Object expected',
67
                [
68 1
                    'schema' => static::class,
69 1
                    'type' => gettype($resource),
70
                ]
71
            );
72
        }
73 1
    }
74
75 1
    private function objectHasSchemaMethod($resource)
76
    {
77 1
        if (!method_exists($resource, 'schema')) {
78
            throw new RestException(
79
                'Object must be a correct RestSchema object',
80
                [
81
                    'schema' => static::class,
82
                    'type' => gettype($resource),
83
                ]
84
            );
85
        }
86 1
    }
87
88 1
    private function objectIsValidSchemaClass($resource)
89
    {
90 1
        if ($this->requiredSchemaClass === null) {
91 1
            return;
92
        }
93
        if (get_class($resource) !== $this->requiredSchemaClass) {
94
            throw new RestException(
95
                'Object must be an instance of the defined SchemaClass',
96
                [
97
                    'schema' => static::class,
98
                    'type' => gettype($resource),
99
                ]
100
            );
101
        }
102
    }
103
104 1
    private function validateRequiredFields($resource)
105
    {
106 1
        $errors = [];
107 1
        if ($this->updateMode()) {
108 1
            return $errors;
109
        }
110
        $requiredFields = $this->getRequiredFields($resource);
111
        foreach ($requiredFields as $fieldName) {
112
            if (!property_exists($resource, $fieldName)) {
113
                $errors[$fieldName] = 'Missing required field';
114
            }
115
        }
116
        return $errors;
117
    }
118
119
    private function validateExtraneousFields($resource)
120
    {
121
        $errors = [];
122
        foreach (array_keys(get_object_vars($resource)) as $fieldName) {
123
            if (!isset($resource->schema()[$fieldName])) {
124
                $errors[$fieldName] = 'Extraneous field';
125
            }
126
        }
127
        return $errors;
128
    }
129
130 1
    private function validateFieldTypes($resource)
131
    {
132 1
        $validatorFactory = new ValidatorFactory;
133 1
        $errors = [];
134 1
        foreach ($resource->schema() as $fieldName => $rules) {
135 1
            if (property_exists($resource, $fieldName)) {
136
                try {
137
                    $validatorFactory->create($rules, $this->updateMode())->validate($resource->{$fieldName});
138
                } catch (RestException $ex) {
139
                    $errors[$fieldName]['error'] = $ex->getMessage();
140 1
                    $errors[$fieldName]['details'] = $ex->getDetails();
141
                }
142
            }
143
        }
144 1
        return $errors;
145
    }
146
147 3
    private function filterFields($resource, $paramKey, $paramValue, $checkParamValue = true)
148
    {
149 3
        $fields = [];
150 3
        foreach ($resource->schema() as $fieldName => $params) {
151 3
            if (array_key_exists($params[$paramKey]) && (!$checkParamValue || $params[$paramKey] == $paramValue)) {
152
                $fields[$fieldName] = $params[$paramKey];
153
            }
154
        }
155
        return $fields;
156
    }
157
158 1
    public function getFields($resource)
159
    {
160 1
        return $resource->schema();
161
    }
162
163
    public function getRequiredFields($resource)
164
    {
165
        return array_keys($this->filterFields($resource, 'required', true));
166
    }
167
168 2
    public function getUniqueFields($resource)
169
    {
170 2
        return array_keys($this->filterFields($resource, 'unique', true));
171
    }
172
173 1
    public function getFieldsWithDefaults($resource)
174
    {
175 1
        return $this->filterFields($resource, 'default', null, false);
176
    }
177
}
178