ValidateResourceObject   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Test Coverage

Coverage 98.51%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 53
dl 0
loc 237
ccs 66
cts 67
cp 0.9851
rs 9.92
c 1
b 0
f 0
wmc 31

8 Methods

Rating   Name   Duplication   Size   Complexity  
A validateResourceObject() 0 21 5
A validateResourceObjectCollection() 0 18 5
A validateResourceTypeMember() 0 15 4
A validateResourceObjectTopLevelStructure() 0 12 2
B validateFields() 0 16 7
A validateResourceLinksObject() 0 3 1
A isNotForbiddenResourceFieldName() 0 4 2
A validateResourceIdMember() 0 16 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace VGirol\JsonApiStructure\Concern;
6
7
use VGirol\JsonApiConstant\Members;
8
use VGirol\JsonApiStructure\Messages;
9
10
/**
11
 * Assertions relating to the resource object
12
 */
13
trait ValidateResourceObject
14
{
15
    /**
16
     * Asserts that a json fragment is a valid collection of resource objects.
17
     *
18
     * It will do the following checks :
19
     * 1) asserts that the provided resource collection is either an empty array or an array of objects
20
     * (@see mustBeArrayOfObjects).
21
     * 2) asserts that the collection of resources is valid (@see validateResourceObject).
22
     *
23
     * @param array|null $json
24
     * @param boolean    $strict If true, unsafe characters are not allowed when checking members name.
25
     *
26
     * @return void
27
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
28
     */
29 36
    public function validateResourceObjectCollection($json, bool $strict): void
30
    {
31 36
        if ($json === null) {
32
            return;
33
        }
34
35 36
        if (!\is_array($json)) {
0 ignored issues
show
introduced by
The condition is_array($json) is always true.
Loading history...
36 3
            $this->throw(Messages::RESOURCE_OBJECT_COLLECTION_MUST_BE_ARRAY, 403);
37
        }
38
39 33
        if (\count($json) == 0) {
40 3
            return;
41
        }
42
43 30
        $this->mustBeArrayOfObjects($json);
0 ignored issues
show
Bug introduced by
It seems like mustBeArrayOfObjects() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

43
        $this->/** @scrutinizer ignore-call */ 
44
               mustBeArrayOfObjects($json);
Loading history...
44
45 21
        foreach ($json as $resource) {
46 21
            $this->validateResourceObject($resource, $strict);
47
        }
48 15
    }
49
50
    /**
51
     * Asserts that a json fragment is a valid resource.
52
     *
53
     * It will do the following checks :
54
     * 1) asserts that the resource object has valid top-level structure
55
     * (@see validateResourceObjectTopLevelStructure).
56
     * 2) asserts that the resource object has valid "type" and "id" members
57
     * (@see validateResourceIdMember and @see validateResourceTypeMember).
58
     * 3) asserts that the resource object has valid fields (@see hasValidFields).
59
     *
60
     * Optionaly, if presents, it will checks :
61
     * 4) asserts thats the resource object has valid "attributes" member.
62
     * 5) asserts thats the resource object has valid "relationships" member.
63
     * 6) asserts thats the resource object has valid "links" member.
64
     * 7) asserts thats the resource object has valid "meta" member.
65
     *
66
     * @param array   $json
67
     * @param boolean $strict If true, unsafe characters are not allowed when checking members name.
68
     *
69
     * @return void
70
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
71
     */
72 93
    public function validateResourceObject($json, bool $strict): void
73
    {
74 93
        $this->validateResourceObjectTopLevelStructure($json, $strict);
75
76 60
        if (\array_key_exists(Members::ATTRIBUTES, $json)) {
77 60
            $this->validateAttributesObject($json[Members::ATTRIBUTES], $strict);
0 ignored issues
show
Bug introduced by
It seems like validateAttributesObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

77
            $this->/** @scrutinizer ignore-call */ 
78
                   validateAttributesObject($json[Members::ATTRIBUTES], $strict);
Loading history...
78
        }
79
80 57
        if (\array_key_exists(Members::RELATIONSHIPS, $json)) {
81 24
            $this->validateRelationshipsObject($json[Members::RELATIONSHIPS], $strict);
0 ignored issues
show
Bug introduced by
It seems like validateRelationshipsObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
            $this->/** @scrutinizer ignore-call */ 
82
                   validateRelationshipsObject($json[Members::RELATIONSHIPS], $strict);
Loading history...
82
        }
83
84 48
        if (\array_key_exists(Members::LINKS, $json)) {
85 6
            $this->validateResourceLinksObject($json[Members::LINKS], $strict);
86
        }
87
88 45
        if (\array_key_exists(Members::META, $json)) {
89 6
            $this->validateMetaObject($json[Members::META], $strict);
0 ignored issues
show
Bug introduced by
It seems like validateMetaObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
            $this->/** @scrutinizer ignore-call */ 
90
                   validateMetaObject($json[Members::META], $strict);
Loading history...
90
        }
91
92 42
        $this->validateFields($json);
93 36
    }
94
95
    /**
96
     * Asserts that a resource object has a valid top-level structure.
97
     *
98
     * It will do the following checks :
99
     * 1) asserts that the resource has an "id" member.
100
     * 2) asserts that the resource has a "type" member.
101
     * 3) asserts that the resource contains at least one of the following members :
102
     * "attributes", "relationships", "links", "meta" (@see containsAtLeastOneMember).
103
     * 4) asserts that the resource contains only the following allowed members :
104
     * "id", "type", "meta", "attributes", "links", "relationships" (@see containsOnlyAllowedMembers).
105
     *
106
     * @param array   $resource
107
     * @param boolean $strict   If true, unsafe characters are not allowed when checking members name.
108
     *
109
     * @return void
110
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
111
     */
112 111
    public function validateResourceObjectTopLevelStructure($resource, $strict): void
113
    {
114 111
        if (!\is_array($resource)) {
0 ignored issues
show
introduced by
The condition is_array($resource) is always true.
Loading history...
115 6
            $this->throw(Messages::RESOURCE_OBJECT_MUST_BE_ARRAY, 403);
116
        }
117
118 105
        $this->validateResourceIdMember($resource);
119 87
        $this->validateResourceTypeMember($resource, $strict);
120
121 78
        $this->containsAtLeastOneMember($this->getRule('ResourceObject.AtLeast'), $resource);
0 ignored issues
show
Bug introduced by
It seems like containsAtLeastOneMember() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
        $this->/** @scrutinizer ignore-call */ 
122
               containsAtLeastOneMember($this->getRule('ResourceObject.AtLeast'), $resource);
Loading history...
Bug introduced by
It seems like getRule() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
        $this->containsAtLeastOneMember($this->/** @scrutinizer ignore-call */ getRule('ResourceObject.AtLeast'), $resource);
Loading history...
122
123 69
        $this->containsOnlyAllowedMembers($this->getRule('ResourceObject.Allowed'), $resource);
0 ignored issues
show
Bug introduced by
It seems like containsOnlyAllowedMembers() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

123
        $this->/** @scrutinizer ignore-call */ 
124
               containsOnlyAllowedMembers($this->getRule('ResourceObject.Allowed'), $resource);
Loading history...
124 63
    }
125
126
    /**
127
     * Asserts that a resource id member is valid.
128
     *
129
     * It will do the following checks :
130
     * 1) asserts that the "id" member is not empty.
131
     * 2) asserts that the "id" member is a string.
132
     *
133
     * @param array $resource
134
     *
135
     * @return void
136
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
137
     */
138 195
    public function validateResourceIdMember($resource): void
139
    {
140 195
        if (!\array_key_exists(Members::ID, $resource)) {
141 18
            if ($this->isPost()) {
0 ignored issues
show
Bug introduced by
It seems like isPost() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
            if ($this->/** @scrutinizer ignore-call */ isPost()) {
Loading history...
142 9
                return;
143
            }
144
145 9
            $this->throw(Messages::RESOURCE_ID_MEMBER_IS_ABSENT, 403);
0 ignored issues
show
Bug introduced by
It seems like throw() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

145
            $this->/** @scrutinizer ignore-call */ 
146
                   throw(Messages::RESOURCE_ID_MEMBER_IS_ABSENT, 403);
Loading history...
146
        }
147
148 177
        if (!\is_string($resource[Members::ID])) {
149 30
            $this->throw(Messages::RESOURCE_ID_MEMBER_MUST_BE_STRING, 403);
150
        }
151
152 147
        if ($resource[Members::ID] == '') {
153 3
            $this->throw(Messages::RESOURCE_ID_MEMBER_CAN_NOT_BE_EMPTY, 403);
154
        }
155 144
    }
156
157
    /**
158
     * Asserts that a resource type member is valid.
159
     *
160
     * It will do the following checks :
161
     * 1) asserts that the "type" member is not empty.
162
     * 2) asserts that the "type" member is a string.
163
     * 3) asserts that the "type" member has a valid value (@see validateMemberName).
164
     *
165
     * @param array   $resource
166
     * @param boolean $strict   If true, excludes not safe characters when checking members name
167
     *
168
     * @return void
169
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
170
     */
171 162
    public function validateResourceTypeMember($resource, bool $strict): void
172
    {
173 162
        if (!\array_key_exists(Members::TYPE, $resource)) {
174 6
            $this->throw(Messages::RESOURCE_TYPE_MEMBER_IS_ABSENT, 403);
175
        }
176
177 156
        if (!\is_string($resource[Members::TYPE])) {
178 12
            $this->throw(Messages::RESOURCE_TYPE_MEMBER_MUST_BE_STRING, 403);
179
        }
180
181 144
        if ($resource[Members::TYPE] == '') {
182 3
            $this->throw(Messages::RESOURCE_TYPE_MEMBER_CAN_NOT_BE_EMPTY, 403);
183
        }
184
185 141
        $this->validateMemberName($resource[Members::TYPE], $strict);
0 ignored issues
show
Bug introduced by
It seems like validateMemberName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

185
        $this->/** @scrutinizer ignore-call */ 
186
               validateMemberName($resource[Members::TYPE], $strict);
Loading history...
186 135
    }
187
188
    /**
189
     * Asserts that a json fragment is a valid resource links object.
190
     *
191
     * It will do the following checks :
192
     * 1) asserts that le links object is valid (@see validateLinksObject) with only "self" member allowed.
193
     *
194
     * @param array   $json
195
     * @param boolean $strict If true, excludes not safe characters when checking members name
196
     *
197
     * @return void
198
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
199
     */
200 12
    public function validateResourceLinksObject($json, bool $strict): void
201
    {
202 12
        $this->validateLinksObject($json, $this->getRule('ResourceObject.LinksObject.Allowed'), $strict);
0 ignored issues
show
Bug introduced by
It seems like validateLinksObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

202
        $this->/** @scrutinizer ignore-call */ 
203
               validateLinksObject($json, $this->getRule('ResourceObject.LinksObject.Allowed'), $strict);
Loading history...
203 6
    }
204
205
    /**
206
     * Asserts that a resource object has valid fields (i.e., resource object’s attributes and its relationships).
207
     *
208
     * It will do the following checks :
209
     * 1) asserts that each attributes member and each relationship name is valid
210
     * (@see isNotForbiddenResourceFieldName)
211
     *
212
     * @param array $resource
213
     *
214
     * @return void
215
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
216
     */
217 54
    public function validateFields($resource): void
218
    {
219 54
        if (\array_key_exists(Members::ATTRIBUTES, $resource)) {
220 54
            foreach (\array_keys($resource[Members::ATTRIBUTES]) as $name) {
221 54
                $this->isNotForbiddenResourceFieldName($name);
222
            }
223
        }
224
225 48
        if (array_key_exists(Members::RELATIONSHIPS, $resource)) {
226 24
            foreach (\array_keys($resource[Members::RELATIONSHIPS]) as $name) {
227 24
                $this->isNotForbiddenResourceFieldName($name);
228
229 21
                if (\array_key_exists(Members::ATTRIBUTES, $resource)
230 21
                    && \array_key_exists($name, $resource[Members::ATTRIBUTES])
231
                ) {
232 6
                    $this->throw(Messages::RESOURCE_FIELDS_CAN_NOT_HAVE_SAME_NAME, 403);
233
                }
234
            }
235
        }
236 39
    }
237
238
    /**
239
     * Asserts that a resource field name is not a forbidden name (like "type" or "id").
240
     *
241
     * @param string $name
242
     *
243
     * @return void
244
     * @throws \VGirol\JsonApiStructure\Exception\ValidationException
245
     */
246 63
    public function isNotForbiddenResourceFieldName(string $name): void
247
    {
248 63
        if (\in_array($name, $this->getRule('ResourceObject.FieldName.Forbidden'))) {
249 15
            $this->throw(Messages::RESOURCE_FIELDS_NAME_NOT_ALLOWED, 403);
250
        }
251 57
    }
252
}
253