| 1 | <?php |
||||||
| 2 | |||||||
| 3 | declare(strict_types=1); |
||||||
| 4 | |||||||
| 5 | namespace VGirol\JsonApiStructure\Concern; |
||||||
| 6 | |||||||
| 7 | use VGirol\JsonApiConstant\Members; |
||||||
| 8 | use VGirol\JsonApiStructure\Constraint\ContainsAtLeastOne; |
||||||
| 9 | use VGirol\JsonApiStructure\Messages; |
||||||
| 10 | |||||||
| 11 | /** |
||||||
| 12 | * Assertions relating to the jsonapi object |
||||||
| 13 | */ |
||||||
| 14 | trait ValidateStructure |
||||||
| 15 | { |
||||||
| 16 | /** |
||||||
| 17 | * Asserts that a json document has valid structure. |
||||||
| 18 | * |
||||||
| 19 | * It will do the following checks : |
||||||
| 20 | * 1) checks top-level members (@see hasValidTopLevelMembers) |
||||||
| 21 | * |
||||||
| 22 | * Optionaly, if presents, it will checks : |
||||||
| 23 | * 2) primary data (@see validatePrimaryData) |
||||||
| 24 | * 3) errors object (@see validateErrorsObject) |
||||||
| 25 | * 4) meta object (@see validateMetaObject) |
||||||
| 26 | * 5) jsonapi object (@see validateJsonapiObject) |
||||||
| 27 | * 6) top-level links object (@see validateTopLevelLinksMember) |
||||||
| 28 | * 7) included object (@see validateIncludedCollection) |
||||||
| 29 | * |
||||||
| 30 | * @param array $json |
||||||
| 31 | * @param boolean $strict If true, unsafe characters are not allowed when checking members name. |
||||||
| 32 | * |
||||||
| 33 | * @return void |
||||||
| 34 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 35 | */ |
||||||
| 36 | 162 | public function validateStructure(array $json, bool $strict) |
|||||
| 37 | { |
||||||
| 38 | 162 | $this->validateTopLevelMembers($json); |
|||||
| 39 | |||||||
| 40 | 141 | if (\array_key_exists(Members::DATA, $json)) { |
|||||
| 41 | 135 | $this->validatePrimaryData($json[Members::DATA], $strict); |
|||||
| 42 | |||||||
| 43 | 39 | if (\array_key_exists(Members::INCLUDED, $json)) { |
|||||
| 44 | 6 | $this->validateIncludedCollection($json[Members::INCLUDED], $json[Members::DATA], $strict); |
|||||
| 45 | } |
||||||
| 46 | } |
||||||
| 47 | |||||||
| 48 | 42 | if (\array_key_exists(Members::LINKS, $json)) { |
|||||
| 49 | 6 | $withPagination = $this->canBePaginated($json); |
|||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||||
| 50 | 6 | $this->validateTopLevelLinksMember($json[Members::LINKS], $withPagination, $strict); |
|||||
| 51 | } |
||||||
| 52 | |||||||
| 53 | $tests = [ |
||||||
| 54 | 39 | Members::META => 'validateMetaObject', |
|||||
| 55 | Members::ERRORS => 'validateErrorsObject', |
||||||
| 56 | Members::JSONAPI => 'validateJsonapiObject' |
||||||
| 57 | ]; |
||||||
| 58 | 39 | foreach ($tests as $member => $func) { |
|||||
| 59 | 39 | if (\array_key_exists($member, $json)) { |
|||||
| 60 | 15 | \call_user_func_array([$this, $func], [$json[$member], $strict]); |
|||||
| 61 | } |
||||||
| 62 | } |
||||||
| 63 | 30 | } |
|||||
| 64 | |||||||
| 65 | /** |
||||||
| 66 | * Asserts that a json document has valid top-level structure. |
||||||
| 67 | * |
||||||
| 68 | * It will do the following checks : |
||||||
| 69 | * 1) asserts that the json document contains at least one of the following top-level members : |
||||||
| 70 | * "data", "meta" or "errors" (@see containsAtLeastOneMember). |
||||||
| 71 | * 2) asserts that the members "data" and "errors" does not coexist in the same document. |
||||||
| 72 | * 3) asserts that the json document contains only the following members : |
||||||
| 73 | * "data", "errors", "meta", "jsonapi", "links", "included" (@see containsOnlyAllowedMembers). |
||||||
| 74 | * 4) if the json document does not contain a top-level "data" member, the "included" member must not |
||||||
| 75 | * be present either. |
||||||
| 76 | |||||||
| 77 | * @param array $json |
||||||
| 78 | * |
||||||
| 79 | * @return void |
||||||
| 80 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 81 | */ |
||||||
| 82 | 177 | public function validateTopLevelMembers(array $json) |
|||||
| 83 | { |
||||||
| 84 | 177 | $expected = $this->getRule('Document.AtLeast'); |
|||||
|
0 ignored issues
–
show
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...
|
|||||||
| 85 | 177 | $this->containsAtLeastOneMember( |
|||||
|
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...
|
|||||||
| 86 | 177 | $expected, |
|||||
| 87 | $json, |
||||||
| 88 | 177 | \sprintf(Messages::DOCUMENT_TOP_LEVEL_MEMBERS, implode('", "', $expected)), |
|||||
| 89 | 177 | 403 |
|||||
| 90 | ); |
||||||
| 91 | |||||||
| 92 | 174 | if (\array_key_exists(Members::DATA, $json) && \array_key_exists(Members::ERRORS, $json)) { |
|||||
| 93 | 3 | $this->throw(Messages::DOCUMENT_DOCUMENT_TOP_LEVEL_MEMBERS_DATA_AND_ERROR, 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...
|
|||||||
| 94 | } |
||||||
| 95 | |||||||
| 96 | 171 | $allowed = $this->getRule('Document.Allowed'); |
|||||
| 97 | 171 | $this->containsOnlyAllowedMembers($allowed, $json); |
|||||
|
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...
|
|||||||
| 98 | |||||||
| 99 | 165 | if (!\array_key_exists(Members::DATA, $json)) { |
|||||
| 100 | 27 | if (\array_key_exists(Members::INCLUDED, $json)) { |
|||||
| 101 | 3 | $this->throw(Messages::DOCUMENT_DOCUMENT_TOP_LEVEL_MEMBERS_DATA_AND_INCLUDED, 403); |
|||||
| 102 | } |
||||||
| 103 | 24 | if (!$this->isAutomatic() && $this->dataIsRequired()) { |
|||||
|
0 ignored issues
–
show
It seems like
dataIsRequired() 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
isAutomatic() 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...
|
|||||||
| 104 | 18 | $this->throw(Messages::REQUEST_ERROR_NO_DATA_MEMBER, 403); |
|||||
| 105 | } |
||||||
| 106 | } |
||||||
| 107 | 144 | } |
|||||
| 108 | |||||||
| 109 | /** |
||||||
| 110 | * Asserts a json fragment is a valid primary data object. |
||||||
| 111 | * |
||||||
| 112 | * It will do the following checks : |
||||||
| 113 | * 1) asserts that the primary data is either an object, an array of objects or the `null` value. |
||||||
| 114 | * 2) if the primary data is not null, checks if it is a valid single resource or a valid resource collection |
||||||
| 115 | * (@see validateResourceObject or @see validateResourceIdentifierObject). |
||||||
| 116 | * |
||||||
| 117 | * @param array|null $json |
||||||
| 118 | * @param boolean $strict If true, unsafe characters are not allowed when checking members name. |
||||||
| 119 | * |
||||||
| 120 | * @return void |
||||||
| 121 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 122 | */ |
||||||
| 123 | 135 | public function validatePrimaryData($json, bool $strict): void |
|||||
| 124 | { |
||||||
| 125 | 135 | if ($json === null) { |
|||||
| 126 | 18 | if (!$this->isAutomatic() && !($this->isRelationshipRoute() && $this->isToOne())) { |
|||||
|
0 ignored issues
–
show
It seems like
isToOne() 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
isRelationshipRoute() 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...
|
|||||||
| 127 | 15 | $this->throw(Messages::REQUEST_ERROR_DATA_MEMBER_NULL, 403); |
|||||
| 128 | } |
||||||
| 129 | 3 | return; |
|||||
| 130 | } |
||||||
| 131 | |||||||
| 132 | 117 | if (!\is_array($json)) { |
|||||
|
0 ignored issues
–
show
|
|||||||
| 133 | 18 | $this->throw(sprintf(Messages::REQUEST_ERROR_DATA_MEMBER_NOT_ARRAY, gettype($json)), 403); |
|||||
| 134 | } |
||||||
| 135 | |||||||
| 136 | 99 | if (\count($json) == 0) { |
|||||
| 137 | 18 | if (!$this->isAutomatic() && !($this->isRelationshipRoute() && $this->isToMany()) |
|||||
|
0 ignored issues
–
show
It seems like
isToMany() 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...
|
|||||||
| 138 | 18 | || ($this->isRelationshipRoute() && $this->isToMany() && ($this->isPost() || $this->isDelete()))) { |
|||||
|
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...
It seems like
isDelete() 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...
|
|||||||
| 139 | 15 | $this->throw( |
|||||
| 140 | 15 | $this->isCollection() ? Messages::REQUEST_ERROR_DATA_MEMBER_NOT_COLLECTION : |
|||||
|
0 ignored issues
–
show
It seems like
isCollection() 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...
|
|||||||
| 141 | 15 | Messages::REQUEST_ERROR_DATA_MEMBER_NOT_SINGLE, |
|||||
| 142 | 15 | 403 |
|||||
| 143 | ); |
||||||
| 144 | } |
||||||
| 145 | 3 | return; |
|||||
| 146 | } |
||||||
| 147 | |||||||
| 148 | 81 | if ($this->isArrayOfObjects($json)) { |
|||||
|
0 ignored issues
–
show
It seems like
isArrayOfObjects() 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...
|
|||||||
| 149 | 30 | if (!$this->isAutomatic() && $this->isSingle()) { |
|||||
|
0 ignored issues
–
show
It seems like
isSingle() 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...
|
|||||||
| 150 | 9 | $this->throw(Messages::REQUEST_ERROR_DATA_MEMBER_NOT_SINGLE, 403); |
|||||
| 151 | } |
||||||
| 152 | |||||||
| 153 | // Resource collection (Resource Objects or Resource Identifier Objects) |
||||||
| 154 | 21 | $this->validatePrimaryCollection($json, true, $strict); |
|||||
| 155 | |||||||
| 156 | 12 | return; |
|||||
| 157 | } |
||||||
| 158 | |||||||
| 159 | 51 | if (!$this->isAutomatic() && $this->isCollection()) { |
|||||
| 160 | 9 | $this->throw(Messages::REQUEST_ERROR_DATA_MEMBER_NOT_COLLECTION, 403); |
|||||
| 161 | } |
||||||
| 162 | |||||||
| 163 | // Single Resource (Resource Object or Resource Identifier Object) |
||||||
| 164 | 42 | $this->validatePrimarySingle($json, $strict); |
|||||
| 165 | 21 | } |
|||||
| 166 | |||||||
| 167 | /** |
||||||
| 168 | * Asserts that a json fragment is a valid top-level links member. |
||||||
| 169 | * |
||||||
| 170 | * It will do the following checks : |
||||||
| 171 | * 1) asserts that the top-level "links" member contains only the following allowed members : |
||||||
| 172 | * "self", "related" and optionaly pagination links (@see validateLinksObject). |
||||||
| 173 | * |
||||||
| 174 | * @param array $json |
||||||
| 175 | * @param boolean $withPagination |
||||||
| 176 | * @param boolean $strict If true, unsafe characters are not allowed when checking members name. |
||||||
| 177 | * |
||||||
| 178 | * @return void |
||||||
| 179 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 180 | */ |
||||||
| 181 | 12 | public function validateTopLevelLinksMember($json, bool $withPagination, bool $strict): void |
|||||
| 182 | { |
||||||
| 183 | 12 | $this->canBePaginated($json); |
|||||
| 184 | 12 | $allowed = $this->getRule('Document.LinksObject.Allowed'); |
|||||
| 185 | 12 | if ($withPagination) { |
|||||
| 186 | 9 | $allowed = array_merge($allowed, $this->getRule('LinksObject.Pagination')); |
|||||
| 187 | } |
||||||
| 188 | 12 | $this->validateLinksObject($json, $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...
|
|||||||
| 189 | 6 | } |
|||||
| 190 | |||||||
| 191 | /** |
||||||
| 192 | * Asserts that a collection of included resources is valid. |
||||||
| 193 | * |
||||||
| 194 | * It will do the following checks : |
||||||
| 195 | * 1) asserts that it is an array of objects (@see isArrayOfObjects). |
||||||
| 196 | * 2) asserts that each resource of the collection is valid (@see validateResourceObject). |
||||||
| 197 | * 3) asserts that each resource in the collection corresponds to an existing resource linkage |
||||||
| 198 | * present in either primary data, primary data relationships or another included resource. |
||||||
| 199 | * 4) asserts that each resource in the collection is unique (i.e. each couple id-type is unique). |
||||||
| 200 | * |
||||||
| 201 | * @param array $included The included top-level member of the json document. |
||||||
| 202 | * @param array $data The primary data of the json document. |
||||||
| 203 | * @param boolean $strict If true, unsafe characters are not allowed when checking members name. |
||||||
| 204 | * |
||||||
| 205 | * @return void |
||||||
| 206 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 207 | */ |
||||||
| 208 | 21 | public function validateIncludedCollection($included, $data, bool $strict): void |
|||||
| 209 | { |
||||||
| 210 | 21 | $this->validateResourceObjectCollection($included, $strict); |
|||||
|
0 ignored issues
–
show
It seems like
validateResourceObjectCollection() 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...
|
|||||||
| 211 | |||||||
| 212 | 12 | $resIdentifiers = \array_merge( |
|||||
| 213 | 12 | $this->getAllResourceIdentifierObjects($data), |
|||||
| 214 | 12 | $this->getAllResourceIdentifierObjects($included) |
|||||
| 215 | ); |
||||||
| 216 | |||||||
| 217 | 12 | $present = []; |
|||||
| 218 | 12 | foreach ($included as $inc) { |
|||||
| 219 | 12 | if (!$this->existsInArray($inc, $resIdentifiers)) { |
|||||
| 220 | 3 | $this->throw(Messages::INCLUDED_RESOURCE_NOT_LINKED, 403); |
|||||
| 221 | } |
||||||
| 222 | |||||||
| 223 | 12 | if (!\array_key_exists($inc[Members::TYPE], $present)) { |
|||||
| 224 | 12 | $present[$inc[Members::TYPE]] = []; |
|||||
| 225 | } |
||||||
| 226 | 12 | if (\in_array($inc[Members::ID], $present[$inc[Members::TYPE]])) { |
|||||
| 227 | 3 | $this->throw(Messages::DOCUMENT_NO_DUPLICATE_RESOURCE, 403); |
|||||
| 228 | } |
||||||
| 229 | |||||||
| 230 | 12 | \array_push($present[$inc[Members::TYPE]], $inc[Members::ID]); |
|||||
| 231 | } |
||||||
| 232 | 6 | } |
|||||
| 233 | |||||||
| 234 | /** |
||||||
| 235 | * Asserts that a collection of resource object is valid. |
||||||
| 236 | * |
||||||
| 237 | * @param array $list |
||||||
| 238 | * @param boolean $checkType If true, asserts that all resources of the collection are of same type |
||||||
| 239 | * @param boolean $strict If true, excludes not safe characters when checking members name |
||||||
| 240 | * |
||||||
| 241 | * @return void |
||||||
| 242 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 243 | */ |
||||||
| 244 | 21 | private function validatePrimaryCollection($list, bool $checkType, bool $strict): void |
|||||
| 245 | { |
||||||
| 246 | 21 | $isResourceObject = null; |
|||||
| 247 | 21 | foreach ($list as $resource) { |
|||||
| 248 | 21 | if ($checkType) { |
|||||
| 249 | // Assert that all resources of the collection are of same type. |
||||||
| 250 | 21 | if ($isResourceObject === null) { |
|||||
| 251 | 21 | $isResourceObject = $this->dataIsResourceObject($resource); |
|||||
| 252 | } |
||||||
| 253 | |||||||
| 254 | 21 | if ($isResourceObject !== $this->dataIsResourceObject($resource)) { |
|||||
| 255 | $this->throw(Messages::PRIMARY_DATA_SAME_TYPE, 403); |
||||||
| 256 | } |
||||||
| 257 | } |
||||||
| 258 | |||||||
| 259 | // Check the resource |
||||||
| 260 | 21 | $this->validatePrimarySingle($resource, $strict); |
|||||
| 261 | } |
||||||
| 262 | 12 | } |
|||||
| 263 | |||||||
| 264 | /** |
||||||
| 265 | * Assert that a single resource object is valid. |
||||||
| 266 | * |
||||||
| 267 | * @param array $resource |
||||||
| 268 | * @param boolean $strict If true, excludes not safe characters when checking members name |
||||||
| 269 | * |
||||||
| 270 | * @return void |
||||||
| 271 | * @throws \VGirol\JsonApiStructure\Exception\ValidationException |
||||||
| 272 | */ |
||||||
| 273 | 63 | private function validatePrimarySingle($resource, bool $strict): void |
|||||
| 274 | { |
||||||
| 275 | 63 | $isResourceObject = $this->isAutomatic() ? |
|||||
| 276 | 18 | $this->dataIsResourceObject($resource) : |
|||||
| 277 | 63 | !$this->isRelationshipRoute(); |
|||||
| 278 | 63 | if ($isResourceObject) { |
|||||
| 279 | 39 | $this->validateResourceObject($resource, $strict); |
|||||
|
0 ignored issues
–
show
It seems like
validateResourceObject() 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...
|
|||||||
| 280 | |||||||
| 281 | 21 | return; |
|||||
| 282 | } |
||||||
| 283 | |||||||
| 284 | 24 | $this->validateResourceIdentifierObject($resource, $strict); |
|||||
|
0 ignored issues
–
show
It seems like
validateResourceIdentifierObject() 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...
|
|||||||
| 285 | 12 | } |
|||||
| 286 | |||||||
| 287 | /** |
||||||
| 288 | * Checks if a given json fragment is a resource object. |
||||||
| 289 | * |
||||||
| 290 | * @param array $resource |
||||||
| 291 | * |
||||||
| 292 | * @return bool |
||||||
| 293 | */ |
||||||
| 294 | 36 | private function dataIsResourceObject($resource): bool |
|||||
| 295 | { |
||||||
| 296 | $expected = [ |
||||||
| 297 | 36 | Members::ATTRIBUTES, |
|||||
| 298 | Members::RELATIONSHIPS, |
||||||
| 299 | Members::LINKS |
||||||
| 300 | ]; |
||||||
| 301 | |||||||
| 302 | 36 | $constraint = new ContainsAtLeastOne($expected); |
|||||
| 303 | |||||||
| 304 | 36 | return $constraint->handle($resource); |
|||||
| 305 | } |
||||||
| 306 | |||||||
| 307 | /** |
||||||
| 308 | * Get all the resource identifier objects (resource linkage) presents in a collection of resource. |
||||||
| 309 | * |
||||||
| 310 | * @param array $data |
||||||
| 311 | * |
||||||
| 312 | * @return array |
||||||
| 313 | */ |
||||||
| 314 | 12 | private function getAllResourceIdentifierObjects($data): array |
|||||
| 315 | { |
||||||
| 316 | 12 | $arr = []; |
|||||
| 317 | 12 | if (\count($data) == 0) { |
|||||
| 318 | return $arr; |
||||||
| 319 | } |
||||||
| 320 | 12 | if (!$this->isArrayOfObjects($data)) { |
|||||
| 321 | 6 | $data = [$data]; |
|||||
| 322 | } |
||||||
| 323 | 12 | foreach ($data as $obj) { |
|||||
| 324 | 12 | if (!\array_key_exists(Members::RELATIONSHIPS, $obj)) { |
|||||
| 325 | 12 | continue; |
|||||
| 326 | } |
||||||
| 327 | 12 | foreach ($obj[Members::RELATIONSHIPS] as $relationship) { |
|||||
| 328 | 12 | if (!\array_key_exists(Members::DATA, $relationship)) { |
|||||
| 329 | 3 | continue; |
|||||
| 330 | } |
||||||
| 331 | 12 | $arr = \array_merge( |
|||||
| 332 | 12 | $arr, |
|||||
| 333 | 12 | $this->isArrayOfObjects($relationship[Members::DATA]) ? |
|||||
| 334 | 12 | $relationship[Members::DATA] : [$relationship[Members::DATA]] |
|||||
| 335 | ); |
||||||
| 336 | } |
||||||
| 337 | } |
||||||
| 338 | |||||||
| 339 | 12 | return $arr; |
|||||
| 340 | } |
||||||
| 341 | |||||||
| 342 | /** |
||||||
| 343 | * Checks if a resource is present in a given array. |
||||||
| 344 | * |
||||||
| 345 | * @param array $needle |
||||||
| 346 | * @param array $arr |
||||||
| 347 | * |
||||||
| 348 | * @return bool |
||||||
| 349 | */ |
||||||
| 350 | 12 | private function existsInArray($needle, $arr): bool |
|||||
| 351 | { |
||||||
| 352 | 12 | foreach ($arr as $resIdentifier) { |
|||||
| 353 | 12 | $test = $resIdentifier[Members::TYPE] === $needle[Members::TYPE] |
|||||
| 354 | 12 | && $resIdentifier[Members::ID] === $needle[Members::ID]; |
|||||
| 355 | 12 | if ($test) { |
|||||
| 356 | 12 | return true; |
|||||
| 357 | } |
||||||
| 358 | } |
||||||
| 359 | |||||||
| 360 | 3 | return false; |
|||||
| 361 | } |
||||||
| 362 | } |
||||||
| 363 |