bankiru /
doctrine-api-client
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Bankiru\Api\Doctrine\Proxy; |
||
| 4 | |||
| 5 | use Bankiru\Api\Doctrine\ApiEntityManager; |
||
| 6 | use Bankiru\Api\Doctrine\Mapping\ApiMetadata; |
||
| 7 | use Doctrine\Common\Collections\AbstractLazyCollection; |
||
| 8 | use Doctrine\Common\Collections\ArrayCollection; |
||
| 9 | use Doctrine\Common\Collections\Collection; |
||
| 10 | use Doctrine\Common\Collections\Criteria; |
||
| 11 | use Doctrine\Common\Collections\Selectable; |
||
| 12 | use ScayTrase\Api\Rpc\Exception\RpcExceptionInterface; |
||
| 13 | |||
| 14 | class ApiCollection extends AbstractLazyCollection implements Selectable |
||
| 15 | { |
||
| 16 | /** @var ApiEntityManager */ |
||
| 17 | private $manager; |
||
| 18 | /** @var ApiMetadata */ |
||
| 19 | private $metadata; |
||
| 20 | /** @var object */ |
||
| 21 | private $owner; |
||
| 22 | /** @var array */ |
||
| 23 | private $association; |
||
| 24 | /** @var bool */ |
||
| 25 | private $isDirty = false; |
||
| 26 | /** @var array */ |
||
| 27 | private $snapshot = []; |
||
| 28 | /** @var string */ |
||
| 29 | private $backRefFieldName; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * ApiCollection constructor. |
||
| 33 | * |
||
| 34 | * @param ApiEntityManager $manager |
||
| 35 | * @param ApiMetadata $class |
||
| 36 | * @param Collection $collection |
||
| 37 | */ |
||
| 38 | public function __construct( |
||
| 39 | ApiEntityManager $manager, |
||
| 40 | ApiMetadata $class, |
||
| 41 | Collection $collection = null |
||
| 42 | ) { |
||
| 43 | $this->manager = $manager; |
||
| 44 | $this->metadata = $class; |
||
| 45 | $this->collection = $collection ?: new ArrayCollection(); |
||
| 46 | $this->initialized = true; |
||
| 47 | } |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @return boolean |
||
| 51 | */ |
||
| 52 | public function isDirty() |
||
| 53 | { |
||
| 54 | return $this->isDirty; |
||
| 55 | } |
||
| 56 | |||
| 57 | public function unwrap() |
||
| 58 | { |
||
| 59 | return $this->collection; |
||
| 60 | } |
||
| 61 | |||
| 62 | /** |
||
| 63 | * INTERNAL: |
||
| 64 | * Tells this collection to take a snapshot of its current state. |
||
| 65 | * |
||
| 66 | * @return void |
||
| 67 | */ |
||
| 68 | public function takeSnapshot() |
||
| 69 | { |
||
| 70 | $this->snapshot = $this->collection->toArray(); |
||
| 71 | $this->isDirty = false; |
||
| 72 | } |
||
| 73 | |||
| 74 | public function getMapping() |
||
| 75 | { |
||
| 76 | return $this->association; |
||
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * @return object |
||
| 81 | */ |
||
| 82 | public function getOwner() |
||
| 83 | { |
||
| 84 | return $this->owner; |
||
| 85 | } |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @param object $owner |
||
| 89 | * @param array $assoc |
||
| 90 | */ |
||
| 91 | public function setOwner($owner, array $assoc) |
||
| 92 | { |
||
| 93 | $this->owner = $owner; |
||
| 94 | $this->association = $assoc; |
||
| 95 | $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; |
||
| 96 | } |
||
| 97 | |||
| 98 | /** |
||
| 99 | * @param bool $dirty |
||
| 100 | */ |
||
| 101 | public function setDirty($dirty) |
||
| 102 | { |
||
| 103 | $this->isDirty = (bool)$dirty; |
||
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Initializes the collection by loading its contents from the database |
||
| 108 | * if the collection is not yet initialized. |
||
| 109 | * |
||
| 110 | * @return void |
||
| 111 | */ |
||
| 112 | public function initialize() |
||
| 113 | { |
||
| 114 | if ($this->initialized || !$this->association) { |
||
|
0 ignored issues
–
show
|
|||
| 115 | return; |
||
| 116 | } |
||
| 117 | $this->doInitialize(); |
||
| 118 | $this->initialized = true; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * @return ApiMetadata |
||
| 123 | */ |
||
| 124 | public function getMetadata() |
||
| 125 | { |
||
| 126 | return $this->metadata; |
||
| 127 | } |
||
| 128 | |||
| 129 | /** |
||
| 130 | * INTERNAL: |
||
| 131 | * Adds an element to a collection during hydration. This will automatically |
||
| 132 | * complete bidirectional associations in the case of a one-to-many association. |
||
| 133 | * |
||
| 134 | * @param mixed $element The element to add. |
||
| 135 | * |
||
| 136 | * @return void |
||
| 137 | */ |
||
| 138 | public function hydrateAdd($element) |
||
| 139 | { |
||
| 140 | $this->collection->add($element); |
||
| 141 | // If _backRefFieldName is set and its a one-to-many association, |
||
| 142 | // we need to set the back reference. |
||
| 143 | if ($this->backRefFieldName && $this->association['type'] === ApiMetadata::ONE_TO_MANY) { |
||
| 144 | // Set back reference to owner |
||
| 145 | $this->metadata->getReflectionProperty($this->backRefFieldName)->setValue( |
||
| 146 | $element, |
||
| 147 | $this->owner |
||
| 148 | ); |
||
| 149 | $this->manager->getUnitOfWork()->setOriginalEntityProperty( |
||
| 150 | spl_object_hash($element), |
||
| 151 | $this->backRefFieldName, |
||
| 152 | $this->owner |
||
| 153 | ); |
||
| 154 | } |
||
| 155 | } |
||
| 156 | |||
| 157 | /** |
||
| 158 | * INTERNAL: |
||
| 159 | * Sets a keyed element in the collection during hydration. |
||
| 160 | * |
||
| 161 | * @param mixed $key The key to set. |
||
| 162 | * @param mixed $element The element to set. |
||
| 163 | * |
||
| 164 | * @return void |
||
| 165 | */ |
||
| 166 | public function hydrateSet($key, $element) |
||
| 167 | { |
||
| 168 | $this->collection->set($key, $element); |
||
| 169 | // If _backRefFieldName is set, then the association is bidirectional |
||
| 170 | // and we need to set the back reference. |
||
| 171 | if ($this->backRefFieldName && $this->association['type'] === ApiMetadata::ONE_TO_MANY) { |
||
| 172 | // Set back reference to owner |
||
| 173 | $this->metadata->getReflectionProperty($this->backRefFieldName)->setValue( |
||
| 174 | $element, |
||
| 175 | $this->owner |
||
| 176 | ); |
||
| 177 | } |
||
| 178 | } |
||
| 179 | |||
| 180 | /** |
||
| 181 | * INTERNAL: |
||
| 182 | * Returns the last snapshot of the elements in the collection. |
||
| 183 | * |
||
| 184 | * @return array The last snapshot of the elements. |
||
| 185 | */ |
||
| 186 | public function getSnapshot() |
||
| 187 | { |
||
| 188 | return $this->snapshot; |
||
| 189 | } |
||
| 190 | |||
| 191 | public function setInitialized($state) |
||
| 192 | { |
||
| 193 | $this->initialized = (bool)$state; |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * INTERNAL: |
||
| 198 | * getDeleteDiff |
||
| 199 | * |
||
| 200 | * @return array |
||
| 201 | */ |
||
| 202 | View Code Duplication | public function getDeleteDiff() |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 203 | { |
||
| 204 | return array_udiff_assoc( |
||
| 205 | $this->snapshot, |
||
| 206 | $this->collection->toArray(), |
||
| 207 | function ($a, $b) { |
||
| 208 | return $a === $b ? 0 : 1; |
||
| 209 | } |
||
| 210 | ); |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * INTERNAL: |
||
| 215 | * getInsertDiff |
||
| 216 | * |
||
| 217 | * @return array |
||
| 218 | */ |
||
| 219 | View Code Duplication | public function getInsertDiff() |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 220 | { |
||
| 221 | return array_udiff_assoc( |
||
| 222 | $this->collection->toArray(), |
||
| 223 | $this->snapshot, |
||
| 224 | function ($a, $b) { |
||
| 225 | return $a === $b ? 0 : 1; |
||
| 226 | } |
||
| 227 | ); |
||
| 228 | } |
||
| 229 | |||
| 230 | /** |
||
| 231 | * {@inheritdoc} |
||
| 232 | */ |
||
| 233 | View Code Duplication | public function remove($key) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 234 | { |
||
| 235 | $removed = parent::remove($key); |
||
| 236 | if (!$removed) { |
||
| 237 | return $removed; |
||
| 238 | } |
||
| 239 | $this->changed(); |
||
| 240 | if ($this->association !== null && |
||
| 241 | $this->association['type'] & ApiMetadata::TO_MANY && |
||
| 242 | $this->owner && |
||
| 243 | $this->association['orphanRemoval'] |
||
| 244 | ) { |
||
| 245 | $this->manager->getUnitOfWork()->scheduleOrphanRemoval($removed); |
||
| 246 | } |
||
| 247 | |||
| 248 | return $removed; |
||
| 249 | } |
||
| 250 | |||
| 251 | /** |
||
| 252 | * {@inheritdoc} |
||
| 253 | */ |
||
| 254 | public function removeElement($element) |
||
| 255 | { |
||
| 256 | if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
||
| 257 | if ($this->collection->contains($element)) { |
||
| 258 | return $this->collection->removeElement($element); |
||
| 259 | } |
||
| 260 | $persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
||
| 261 | if ($persister->removeElement($this, $element)) { |
||
|
0 ignored issues
–
show
The method
removeElement() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 262 | return $element; |
||
| 263 | } |
||
| 264 | |||
| 265 | return null; |
||
| 266 | } |
||
| 267 | $removed = parent::removeElement($element); |
||
| 268 | if (!$removed) { |
||
| 269 | return $removed; |
||
| 270 | } |
||
| 271 | $this->changed(); |
||
| 272 | if ($this->association !== null && |
||
| 273 | $this->association['type'] & ApiMetadata::TO_MANY && |
||
| 274 | $this->owner && |
||
| 275 | $this->association['orphanRemoval'] |
||
| 276 | ) { |
||
| 277 | $this->manager->getUnitOfWork()->scheduleOrphanRemoval($element); |
||
| 278 | } |
||
| 279 | |||
| 280 | return $removed; |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * {@inheritdoc} |
||
| 285 | */ |
||
| 286 | View Code Duplication | public function containsKey($key) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 287 | { |
||
| 288 | if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY |
||
| 289 | && isset($this->association['indexBy']) |
||
| 290 | ) { |
||
| 291 | $persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
||
| 292 | |||
| 293 | return $this->collection->containsKey($key) || $persister->containsKey($this, $key); |
||
|
0 ignored issues
–
show
The method
containsKey() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 294 | } |
||
| 295 | |||
| 296 | return parent::containsKey($key); |
||
| 297 | } |
||
| 298 | |||
| 299 | /** |
||
| 300 | * {@inheritdoc} |
||
| 301 | */ |
||
| 302 | View Code Duplication | public function contains($element) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 303 | { |
||
| 304 | if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
||
| 305 | $persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
||
| 306 | |||
| 307 | return $this->collection->contains($element) || $persister->contains($this, $element); |
||
|
0 ignored issues
–
show
The method
contains() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 308 | } |
||
| 309 | |||
| 310 | return parent::contains($element); |
||
| 311 | } |
||
| 312 | |||
| 313 | /** |
||
| 314 | * {@inheritdoc} |
||
| 315 | */ |
||
| 316 | public function get($key) |
||
| 317 | { |
||
| 318 | if (!$this->initialized |
||
| 319 | && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY |
||
| 320 | && isset($this->association['indexBy']) |
||
| 321 | ) { |
||
| 322 | if (!$this->metadata->isIdentifierComposite() && |
||
| 323 | $this->metadata->isIdentifier($this->association['indexBy']) |
||
| 324 | ) { |
||
| 325 | return $this->manager->find($this->metadata->getName(), $key); |
||
| 326 | } |
||
| 327 | |||
| 328 | return $this->manager->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); |
||
|
0 ignored issues
–
show
The method
get() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 329 | } |
||
| 330 | |||
| 331 | return parent::get($key); |
||
| 332 | } |
||
| 333 | |||
| 334 | /** |
||
| 335 | * {@inheritdoc} |
||
| 336 | */ |
||
| 337 | public function count() |
||
| 338 | { |
||
| 339 | if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
||
| 340 | $persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
||
| 341 | |||
| 342 | return $persister->count($this) + ($this->isDirty ? $this->collection->count() : 0); |
||
|
0 ignored issues
–
show
The method
count() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 343 | } |
||
| 344 | |||
| 345 | return parent::count(); |
||
| 346 | } |
||
| 347 | |||
| 348 | /** |
||
| 349 | * {@inheritdoc} |
||
| 350 | */ |
||
| 351 | View Code Duplication | public function set($key, $value) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 352 | { |
||
| 353 | parent::set($key, $value); |
||
| 354 | $this->changed(); |
||
| 355 | if (is_object($value) && $this->manager) { |
||
| 356 | $this->manager->getUnitOfWork()->cancelOrphanRemoval($value); |
||
| 357 | } |
||
| 358 | } |
||
| 359 | |||
| 360 | /** |
||
| 361 | * {@inheritdoc} |
||
| 362 | */ |
||
| 363 | View Code Duplication | public function add($value) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 364 | { |
||
| 365 | $this->collection->add($value); |
||
| 366 | $this->changed(); |
||
| 367 | if (is_object($value) && $this->manager) { |
||
| 368 | $this->manager->getUnitOfWork()->cancelOrphanRemoval($value); |
||
| 369 | } |
||
| 370 | |||
| 371 | return true; |
||
| 372 | } |
||
| 373 | |||
| 374 | /** |
||
| 375 | * {@inheritdoc} |
||
| 376 | */ |
||
| 377 | public function offsetExists($offset) |
||
| 378 | { |
||
| 379 | return $this->containsKey($offset); |
||
| 380 | } |
||
| 381 | |||
| 382 | /** |
||
| 383 | * {@inheritdoc} |
||
| 384 | */ |
||
| 385 | public function offsetGet($offset) |
||
| 386 | { |
||
| 387 | return $this->get($offset); |
||
| 388 | } |
||
| 389 | /* ArrayAccess implementation */ |
||
| 390 | |||
| 391 | /** |
||
| 392 | * {@inheritdoc} |
||
| 393 | */ |
||
| 394 | public function offsetSet($offset, $value) |
||
| 395 | { |
||
| 396 | if (!isset($offset)) { |
||
| 397 | return $this->add($value); |
||
| 398 | } |
||
| 399 | |||
| 400 | return $this->set($offset, $value); |
||
| 401 | } |
||
| 402 | |||
| 403 | /** |
||
| 404 | * {@inheritdoc} |
||
| 405 | */ |
||
| 406 | public function offsetUnset($offset) |
||
| 407 | { |
||
| 408 | return $this->remove($offset); |
||
| 409 | } |
||
| 410 | |||
| 411 | /** |
||
| 412 | * {@inheritdoc} |
||
| 413 | */ |
||
| 414 | public function isEmpty() |
||
| 415 | { |
||
| 416 | return $this->collection->isEmpty() && $this->count() === 0; |
||
| 417 | } |
||
| 418 | |||
| 419 | /** |
||
| 420 | * {@inheritdoc} |
||
| 421 | */ |
||
| 422 | public function clear() |
||
| 423 | { |
||
| 424 | if ($this->initialized && $this->isEmpty()) { |
||
| 425 | return; |
||
| 426 | } |
||
| 427 | $uow = $this->manager->getUnitOfWork(); |
||
| 428 | if ($this->association['type'] & ApiMetadata::TO_MANY && |
||
| 429 | $this->association['orphanRemoval'] && |
||
| 430 | $this->owner |
||
| 431 | ) { |
||
| 432 | // we need to initialize here, as orphan removal acts like implicit cascadeRemove, |
||
| 433 | // hence for event listeners we need the objects in memory. |
||
| 434 | $this->initialize(); |
||
| 435 | foreach ($this->collection as $element) { |
||
| 436 | $uow->scheduleOrphanRemoval($element); |
||
| 437 | } |
||
| 438 | } |
||
| 439 | $this->collection->clear(); |
||
| 440 | $this->initialized = true; // direct call, {@link initialize()} is too expensive |
||
| 441 | if ($this->association['isOwningSide'] && $this->owner) { |
||
| 442 | $this->changed(); |
||
| 443 | $uow->scheduleCollectionDeletion($this); |
||
| 444 | $this->takeSnapshot(); |
||
| 445 | } |
||
| 446 | } |
||
| 447 | |||
| 448 | /** |
||
| 449 | * Called by PHP when this collection is serialized. Ensures that only the |
||
| 450 | * elements are properly serialized. |
||
| 451 | * |
||
| 452 | * Internal note: Tried to implement Serializable first but that did not work well |
||
| 453 | * with circular references. This solution seems simpler and works well. |
||
| 454 | * |
||
| 455 | * @return array |
||
| 456 | */ |
||
| 457 | public function __sleep() |
||
| 458 | { |
||
| 459 | return ['collection', 'initialized']; |
||
| 460 | } |
||
| 461 | |||
| 462 | /** |
||
| 463 | * Extracts a slice of $length elements starting at position $offset from the Collection. |
||
| 464 | * |
||
| 465 | * If $length is null it returns all elements from $offset to the end of the Collection. |
||
| 466 | * Keys have to be preserved by this method. Calling this method will only return the |
||
| 467 | * selected slice and NOT change the elements contained in the collection slice is called on. |
||
| 468 | * |
||
| 469 | * @param int $offset |
||
| 470 | * @param int|null $length |
||
| 471 | * |
||
| 472 | * @return array |
||
| 473 | */ |
||
| 474 | View Code Duplication | public function slice($offset, $length = null) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 475 | { |
||
| 476 | if (!$this->initialized && !$this->isDirty && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
||
| 477 | $persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
||
| 478 | |||
| 479 | return $persister->slice($this, $offset, $length); |
||
|
0 ignored issues
–
show
The method
slice() does not seem to exist on object<Bankiru\Api\Doctr...er\CollectionPersister>.
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||
| 480 | } |
||
| 481 | |||
| 482 | return parent::slice($offset, $length); |
||
| 483 | } |
||
| 484 | |||
| 485 | /** |
||
| 486 | * Cleans up internal state of cloned persistent collection. |
||
| 487 | * |
||
| 488 | * The following problems have to be prevented: |
||
| 489 | * 1. Added entities are added to old PC |
||
| 490 | * 2. New collection is not dirty, if reused on other entity nothing |
||
| 491 | * changes. |
||
| 492 | * 3. Snapshot leads to invalid diffs being generated. |
||
| 493 | * 4. Lazy loading grabs entities from old owner object. |
||
| 494 | * 5. New collection is connected to old owner and leads to duplicate keys. |
||
| 495 | * |
||
| 496 | * @return void |
||
| 497 | */ |
||
| 498 | public function __clone() |
||
| 499 | { |
||
| 500 | if (is_object($this->collection)) { |
||
| 501 | $this->collection = clone $this->collection; |
||
| 502 | } |
||
| 503 | $this->initialize(); |
||
| 504 | $this->owner = null; |
||
| 505 | $this->snapshot = []; |
||
| 506 | $this->changed(); |
||
| 507 | } |
||
| 508 | |||
| 509 | /** |
||
| 510 | * Selects all elements from a selectable that match the expression and |
||
| 511 | * return a new collection containing these elements. |
||
| 512 | * |
||
| 513 | * @param \Doctrine\Common\Collections\Criteria $criteria |
||
| 514 | * |
||
| 515 | * @return Collection |
||
| 516 | * |
||
| 517 | * @throws \RuntimeException |
||
| 518 | */ |
||
| 519 | public function matching(Criteria $criteria) |
||
| 520 | { |
||
| 521 | if ($this->isDirty) { |
||
| 522 | $this->initialize(); |
||
| 523 | } |
||
| 524 | if ($this->initialized) { |
||
| 525 | return $this->collection->matching($criteria); |
||
|
0 ignored issues
–
show
It seems like you code against a concrete implementation and not the interface
Doctrine\Common\Collections\Collection as the method matching() does only exist in the following implementations of said interface: Bankiru\Api\Doctrine\Proxy\ApiCollection, Doctrine\Common\Collections\ArrayCollection.
Let’s take a look at an example: interface User
{
/** @return string */
public function getPassword();
}
class MyUser implements User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 526 | } |
||
| 527 | |||
| 528 | $builder = Criteria::expr(); |
||
| 529 | $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); |
||
| 530 | $expression = $criteria->getWhereExpression(); |
||
| 531 | $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; |
||
| 532 | $criteria = clone $criteria; |
||
| 533 | $criteria->where($expression); |
||
| 534 | $persister = $this->manager->getUnitOfWork()->getEntityPersister($this->association['target']); |
||
| 535 | |||
| 536 | return new LazyCriteriaCollection($persister, $criteria); |
||
| 537 | } |
||
| 538 | |||
| 539 | /** |
||
| 540 | * Do the initialization logic |
||
| 541 | * |
||
| 542 | * @return void |
||
| 543 | * @throws RpcExceptionInterface |
||
| 544 | */ |
||
| 545 | protected function doInitialize() |
||
| 546 | { |
||
| 547 | // Has NEW objects added through add(). Remember them. |
||
| 548 | $newObjects = []; |
||
| 549 | if ($this->isDirty) { |
||
| 550 | $newObjects = $this->collection->toArray(); |
||
| 551 | } |
||
| 552 | $this->collection->clear(); |
||
| 553 | $this->manager->getUnitOfWork()->loadCollection($this); |
||
| 554 | $this->takeSnapshot(); |
||
| 555 | // Reattach NEW objects added through add(), if any. |
||
| 556 | if ($newObjects) { |
||
|
0 ignored issues
–
show
The expression
$newObjects of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 557 | foreach ($newObjects as $obj) { |
||
| 558 | $this->collection->add($obj); |
||
| 559 | } |
||
| 560 | $this->isDirty = true; |
||
| 561 | } |
||
| 562 | } |
||
| 563 | |||
| 564 | /** |
||
| 565 | * Marks this collection as changed/dirty. |
||
| 566 | * |
||
| 567 | * @return void |
||
| 568 | */ |
||
| 569 | private function changed() |
||
| 570 | { |
||
| 571 | $this->isDirty = true; |
||
| 572 | } |
||
| 573 | } |
||
| 574 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.