| 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
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
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
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
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
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
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
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
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
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
|
|||||||||
| 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
Loading history...
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
Loading history...
|
|||||||||
| 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
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
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
Loading history...
|
|||||||||
| 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
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
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
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
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
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 |