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
![]() |
|||||||||
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
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
![]() |
|||||||||
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
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
![]() |
|||||||||
78 | } |
||||||||
79 | |||||||||
80 | 57 | if (\array_key_exists(Members::RELATIONSHIPS, $json)) { |
|||||||
81 | 24 | $this->validateRelationshipsObject($json[Members::RELATIONSHIPS], $strict); |
|||||||
0 ignored issues
–
show
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
![]() |
|||||||||
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
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
![]() |
|||||||||
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
|
|||||||||
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
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
![]() 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
![]() |
|||||||||
122 | |||||||||
123 | 69 | $this->containsOnlyAllowedMembers($this->getRule('ResourceObject.Allowed'), $resource); |
|||||||
0 ignored issues
–
show
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
![]() |
|||||||||
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
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
![]() |
|||||||||
142 | 9 | return; |
|||||||
143 | } |
||||||||
144 | |||||||||
145 | 9 | $this->throw(Messages::RESOURCE_ID_MEMBER_IS_ABSENT, 403); |
|||||||
0 ignored issues
–
show
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
![]() |
|||||||||
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
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
![]() |
|||||||||
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
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
![]() |
|||||||||
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 |