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 Doctrine\Tests\ORM; |
||
4 | |||
5 | use Doctrine\Common\Collections\ArrayCollection; |
||
6 | use Doctrine\Common\EventManager; |
||
7 | use Doctrine\Common\NotifyPropertyChanged; |
||
8 | use Doctrine\Common\Persistence\Event\LifecycleEventArgs; |
||
9 | use Doctrine\Common\PropertyChangedListener; |
||
10 | use Doctrine\ORM\Events; |
||
11 | use Doctrine\ORM\Mapping\ClassMetadata; |
||
12 | use Doctrine\ORM\ORMInvalidArgumentException; |
||
13 | use Doctrine\ORM\UnitOfWork; |
||
14 | use Doctrine\Tests\Mocks\ConnectionMock; |
||
15 | use Doctrine\Tests\Mocks\DriverMock; |
||
16 | use Doctrine\Tests\Mocks\EntityManagerMock; |
||
17 | use Doctrine\Tests\Mocks\EntityPersisterMock; |
||
18 | use Doctrine\Tests\Mocks\UnitOfWorkMock; |
||
19 | use Doctrine\Tests\Models\CMS\CmsPhonenumber; |
||
20 | use Doctrine\Tests\Models\CMS\CmsUser; |
||
21 | use Doctrine\Tests\Models\Forum\ForumAvatar; |
||
22 | use Doctrine\Tests\Models\Forum\ForumUser; |
||
23 | use Doctrine\Tests\Models\GeoNames\City; |
||
24 | use Doctrine\Tests\Models\GeoNames\Country; |
||
25 | use Doctrine\Tests\OrmTestCase; |
||
26 | use stdClass; |
||
27 | |||
28 | /** |
||
29 | * UnitOfWork tests. |
||
30 | */ |
||
31 | class UnitOfWorkTest extends OrmTestCase |
||
32 | { |
||
33 | /** |
||
34 | * SUT |
||
35 | * |
||
36 | * @var UnitOfWorkMock |
||
37 | */ |
||
38 | private $_unitOfWork; |
||
39 | |||
40 | /** |
||
41 | * Provides a sequence mock to the UnitOfWork |
||
42 | * |
||
43 | * @var ConnectionMock |
||
44 | */ |
||
45 | private $_connectionMock; |
||
46 | |||
47 | /** |
||
48 | * The EntityManager mock that provides the mock persisters |
||
49 | * |
||
50 | * @var EntityManagerMock |
||
51 | */ |
||
52 | private $_emMock; |
||
53 | |||
54 | /** |
||
55 | * @var EventManager|\PHPUnit_Framework_MockObject_MockObject |
||
56 | */ |
||
57 | private $eventManager; |
||
58 | |||
59 | protected function setUp() |
||
60 | { |
||
61 | parent::setUp(); |
||
62 | $this->_connectionMock = new ConnectionMock([], new DriverMock()); |
||
63 | $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock(); |
||
64 | $this->_emMock = EntityManagerMock::create($this->_connectionMock, null, $this->eventManager); |
||
65 | // SUT |
||
66 | $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); |
||
67 | $this->_emMock->setUnitOfWork($this->_unitOfWork); |
||
68 | } |
||
69 | |||
70 | public function testRegisterRemovedOnNewEntityIsIgnored() |
||
71 | { |
||
72 | $user = new ForumUser(); |
||
73 | $user->username = 'romanb'; |
||
74 | $this->assertFalse($this->_unitOfWork->isScheduledForDelete($user)); |
||
75 | $this->_unitOfWork->scheduleForDelete($user); |
||
76 | $this->assertFalse($this->_unitOfWork->isScheduledForDelete($user)); |
||
77 | } |
||
78 | |||
79 | |||
80 | /* Operational tests */ |
||
81 | |||
82 | public function testSavingSingleEntityWithIdentityColumnForcesInsert() |
||
83 | { |
||
84 | // Setup fake persister and id generator for identity generation |
||
85 | $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class)); |
||
86 | $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister); |
||
87 | $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); |
||
88 | |||
89 | // Test |
||
90 | $user = new ForumUser(); |
||
91 | $user->username = 'romanb'; |
||
92 | $this->_unitOfWork->persist($user); |
||
93 | |||
94 | // Check |
||
95 | $this->assertEquals(0, count($userPersister->getInserts())); |
||
96 | $this->assertEquals(0, count($userPersister->getUpdates())); |
||
97 | $this->assertEquals(0, count($userPersister->getDeletes())); |
||
98 | $this->assertFalse($this->_unitOfWork->isInIdentityMap($user)); |
||
99 | // should no longer be scheduled for insert |
||
100 | $this->assertTrue($this->_unitOfWork->isScheduledForInsert($user)); |
||
101 | |||
102 | // Now lets check whether a subsequent commit() does anything |
||
103 | $userPersister->reset(); |
||
104 | |||
105 | // Test |
||
106 | $this->_unitOfWork->commit(); |
||
107 | |||
108 | // Check. |
||
109 | $this->assertEquals(1, count($userPersister->getInserts())); |
||
110 | $this->assertEquals(0, count($userPersister->getUpdates())); |
||
111 | $this->assertEquals(0, count($userPersister->getDeletes())); |
||
112 | |||
113 | // should have an id |
||
114 | $this->assertTrue(is_numeric($user->id)); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Tests a scenario where a save() operation is cascaded from a ForumUser |
||
119 | * to its associated ForumAvatar, both entities using IDENTITY id generation. |
||
120 | */ |
||
121 | public function testCascadedIdentityColumnInsert() |
||
122 | { |
||
123 | // Setup fake persister and id generator for identity generation |
||
124 | //ForumUser |
||
125 | $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class)); |
||
126 | $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister); |
||
127 | $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); |
||
128 | // ForumAvatar |
||
129 | $avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumAvatar::class)); |
||
130 | $this->_unitOfWork->setEntityPersister(ForumAvatar::class, $avatarPersister); |
||
131 | $avatarPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); |
||
132 | |||
133 | // Test |
||
134 | $user = new ForumUser(); |
||
135 | $user->username = 'romanb'; |
||
136 | $avatar = new ForumAvatar(); |
||
137 | $user->avatar = $avatar; |
||
138 | $this->_unitOfWork->persist($user); // save cascaded to avatar |
||
139 | |||
140 | $this->_unitOfWork->commit(); |
||
141 | |||
142 | $this->assertTrue(is_numeric($user->id)); |
||
143 | $this->assertTrue(is_numeric($avatar->id)); |
||
144 | |||
145 | $this->assertEquals(1, count($userPersister->getInserts())); |
||
146 | $this->assertEquals(0, count($userPersister->getUpdates())); |
||
147 | $this->assertEquals(0, count($userPersister->getDeletes())); |
||
148 | |||
149 | $this->assertEquals(1, count($avatarPersister->getInserts())); |
||
150 | $this->assertEquals(0, count($avatarPersister->getUpdates())); |
||
151 | $this->assertEquals(0, count($avatarPersister->getDeletes())); |
||
152 | } |
||
153 | |||
154 | public function testChangeTrackingNotify() |
||
155 | { |
||
156 | $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(NotifyChangedEntity::class)); |
||
157 | $this->_unitOfWork->setEntityPersister(NotifyChangedEntity::class, $persister); |
||
158 | $itemPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(NotifyChangedRelatedItem::class)); |
||
159 | $this->_unitOfWork->setEntityPersister(NotifyChangedRelatedItem::class, $itemPersister); |
||
160 | |||
161 | $entity = new NotifyChangedEntity; |
||
162 | $entity->setData('thedata'); |
||
163 | $this->_unitOfWork->persist($entity); |
||
164 | |||
165 | $this->_unitOfWork->commit(); |
||
166 | $this->assertEquals(1, count($persister->getInserts())); |
||
167 | $persister->reset(); |
||
168 | |||
169 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity)); |
||
170 | |||
171 | $entity->setData('newdata'); |
||
172 | $entity->setTransient('newtransientvalue'); |
||
173 | |||
174 | $this->assertTrue($this->_unitOfWork->isScheduledForDirtyCheck($entity)); |
||
175 | |||
176 | $this->assertEquals(['data' => ['thedata', 'newdata']], $this->_unitOfWork->getEntityChangeSet($entity)); |
||
177 | |||
178 | $item = new NotifyChangedRelatedItem(); |
||
179 | $entity->getItems()->add($item); |
||
180 | $item->setOwner($entity); |
||
181 | $this->_unitOfWork->persist($item); |
||
182 | |||
183 | $this->_unitOfWork->commit(); |
||
184 | $this->assertEquals(1, count($itemPersister->getInserts())); |
||
185 | $persister->reset(); |
||
186 | $itemPersister->reset(); |
||
187 | |||
188 | |||
189 | $entity->getItems()->removeElement($item); |
||
190 | $item->setOwner(null); |
||
191 | $this->assertTrue($entity->getItems()->isDirty()); |
||
0 ignored issues
–
show
|
|||
192 | $this->_unitOfWork->commit(); |
||
193 | $updates = $itemPersister->getUpdates(); |
||
194 | $this->assertEquals(1, count($updates)); |
||
195 | $this->assertTrue($updates[0] === $item); |
||
196 | } |
||
197 | |||
198 | View Code Duplication | public function testGetEntityStateOnVersionedEntityWithAssignedIdentifier() |
|
199 | { |
||
200 | $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(VersionedAssignedIdentifierEntity::class)); |
||
201 | $this->_unitOfWork->setEntityPersister(VersionedAssignedIdentifierEntity::class, $persister); |
||
202 | |||
203 | $e = new VersionedAssignedIdentifierEntity(); |
||
204 | $e->id = 42; |
||
205 | $this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($e)); |
||
206 | $this->assertFalse($persister->isExistsCalled()); |
||
207 | } |
||
208 | |||
209 | public function testGetEntityStateWithAssignedIdentity() |
||
210 | { |
||
211 | $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CmsPhonenumber::class)); |
||
212 | $this->_unitOfWork->setEntityPersister(CmsPhonenumber::class, $persister); |
||
213 | |||
214 | $ph = new CmsPhonenumber(); |
||
215 | $ph->phonenumber = '12345'; |
||
216 | |||
217 | $this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($ph)); |
||
218 | $this->assertTrue($persister->isExistsCalled()); |
||
219 | |||
220 | $persister->reset(); |
||
221 | |||
222 | // if the entity is already managed the exists() check should be skipped |
||
223 | $this->_unitOfWork->registerManaged($ph, ['phonenumber' => '12345'], []); |
||
224 | $this->assertEquals(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($ph)); |
||
225 | $this->assertFalse($persister->isExistsCalled()); |
||
226 | $ph2 = new CmsPhonenumber(); |
||
227 | $ph2->phonenumber = '12345'; |
||
228 | $this->assertEquals(UnitOfWork::STATE_DETACHED, $this->_unitOfWork->getEntityState($ph2)); |
||
229 | $this->assertFalse($persister->isExistsCalled()); |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * DDC-2086 [GH-484] Prevented 'Undefined index' notice when updating. |
||
234 | */ |
||
235 | public function testNoUndefinedIndexNoticeOnScheduleForUpdateWithoutChanges() |
||
236 | { |
||
237 | // Setup fake persister and id generator |
||
238 | $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class)); |
||
239 | $userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); |
||
240 | $this->_unitOfWork->setEntityPersister(ForumUser::class, $userPersister); |
||
241 | |||
242 | // Create a test user |
||
243 | $user = new ForumUser(); |
||
244 | $user->name = 'Jasper'; |
||
0 ignored issues
–
show
The property
name does not seem to exist in Doctrine\Tests\Models\Forum\ForumUser .
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
245 | $this->_unitOfWork->persist($user); |
||
246 | $this->_unitOfWork->commit(); |
||
247 | |||
248 | // Schedule user for update without changes |
||
249 | $this->_unitOfWork->scheduleForUpdate($user); |
||
250 | |||
251 | self::assertNotEmpty($this->_unitOfWork->getScheduledEntityUpdates()); |
||
252 | |||
253 | // This commit should not raise an E_NOTICE |
||
254 | $this->_unitOfWork->commit(); |
||
255 | |||
256 | self::assertEmpty($this->_unitOfWork->getScheduledEntityUpdates()); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * @group DDC-1984 |
||
261 | */ |
||
262 | public function testLockWithoutEntityThrowsException() |
||
263 | { |
||
264 | $this->expectException(\InvalidArgumentException::class); |
||
265 | $this->_unitOfWork->lock(null, null, null); |
||
0 ignored issues
–
show
null is of type null , but the function expects a object .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
Loading history...
|
|||
266 | } |
||
267 | |||
268 | /** |
||
269 | * @group DDC-3490 |
||
270 | * |
||
271 | * @dataProvider invalidAssociationValuesDataProvider |
||
272 | * |
||
273 | * @param mixed $invalidValue |
||
274 | */ |
||
275 | View Code Duplication | public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue($invalidValue) |
|
276 | { |
||
277 | $this->_unitOfWork->setEntityPersister( |
||
278 | ForumUser::class, |
||
279 | new EntityPersisterMock( |
||
280 | $this->_emMock, |
||
281 | $this->_emMock->getClassMetadata(ForumUser::class) |
||
282 | ) |
||
283 | ); |
||
284 | |||
285 | $user = new ForumUser(); |
||
286 | $user->username = 'John'; |
||
287 | $user->avatar = $invalidValue; |
||
288 | |||
289 | $this->expectException(\Doctrine\ORM\ORMInvalidArgumentException::class); |
||
290 | |||
291 | $this->_unitOfWork->persist($user); |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * @group DDC-3490 |
||
296 | * |
||
297 | * @dataProvider invalidAssociationValuesDataProvider |
||
298 | * |
||
299 | * @param mixed $invalidValue |
||
300 | */ |
||
301 | public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationValue($invalidValue) |
||
302 | { |
||
303 | $metadata = $this->_emMock->getClassMetadata(ForumUser::class); |
||
304 | |||
305 | $this->_unitOfWork->setEntityPersister( |
||
306 | ForumUser::class, |
||
307 | new EntityPersisterMock($this->_emMock, $metadata) |
||
308 | ); |
||
309 | |||
310 | $user = new ForumUser(); |
||
311 | |||
312 | $this->_unitOfWork->persist($user); |
||
313 | |||
314 | $user->username = 'John'; |
||
315 | $user->avatar = $invalidValue; |
||
316 | |||
317 | $this->expectException(\Doctrine\ORM\ORMInvalidArgumentException::class); |
||
318 | |||
319 | $this->_unitOfWork->computeChangeSet($metadata, $user); |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * @group DDC-3619 |
||
324 | * @group 1338 |
||
325 | */ |
||
326 | public function testRemovedAndRePersistedEntitiesAreInTheIdentityMapAndAreNotGarbageCollected() |
||
327 | { |
||
328 | $entity = new ForumUser(); |
||
329 | $entity->id = 123; |
||
330 | |||
331 | $this->_unitOfWork->registerManaged($entity, ['id' => 123], []); |
||
332 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity)); |
||
333 | |||
334 | $this->_unitOfWork->remove($entity); |
||
335 | $this->assertFalse($this->_unitOfWork->isInIdentityMap($entity)); |
||
336 | |||
337 | $this->_unitOfWork->persist($entity); |
||
338 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity)); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * @group 5849 |
||
343 | * @group 5850 |
||
344 | */ |
||
345 | public function testPersistedEntityAndClearManager() |
||
346 | { |
||
347 | $entity1 = new City(123, 'London'); |
||
348 | $entity2 = new Country(456, 'United Kingdom'); |
||
349 | |||
350 | $this->_unitOfWork->persist($entity1); |
||
351 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity1)); |
||
352 | |||
353 | $this->_unitOfWork->persist($entity2); |
||
354 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity2)); |
||
355 | |||
356 | $this->_unitOfWork->clear(Country::class); |
||
357 | $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity1)); |
||
358 | $this->assertFalse($this->_unitOfWork->isInIdentityMap($entity2)); |
||
359 | $this->assertTrue($this->_unitOfWork->isScheduledForInsert($entity1)); |
||
360 | $this->assertFalse($this->_unitOfWork->isScheduledForInsert($entity2)); |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Data Provider |
||
365 | * |
||
366 | * @return mixed[][] |
||
367 | */ |
||
368 | public function invalidAssociationValuesDataProvider() |
||
369 | { |
||
370 | return [ |
||
371 | ['foo'], |
||
372 | [['foo']], |
||
373 | [''], |
||
374 | [[]], |
||
375 | [new stdClass()], |
||
376 | [new ArrayCollection()], |
||
377 | ]; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * @dataProvider entitiesWithValidIdentifiersProvider |
||
382 | * |
||
383 | * @param object $entity |
||
384 | * @param string $idHash |
||
385 | * |
||
386 | * @return void |
||
387 | */ |
||
388 | public function testAddToIdentityMapValidIdentifiers($entity, $idHash) |
||
389 | { |
||
390 | $this->_unitOfWork->persist($entity); |
||
391 | $this->_unitOfWork->addToIdentityMap($entity); |
||
392 | |||
393 | self::assertSame($entity, $this->_unitOfWork->getByIdHash($idHash, get_class($entity))); |
||
394 | } |
||
395 | |||
396 | public function entitiesWithValidIdentifiersProvider() |
||
397 | { |
||
398 | $emptyString = new EntityWithStringIdentifier(); |
||
399 | |||
400 | $emptyString->id = ''; |
||
401 | |||
402 | $nonEmptyString = new EntityWithStringIdentifier(); |
||
403 | |||
404 | $nonEmptyString->id = uniqid('id', true); |
||
405 | |||
406 | $emptyStrings = new EntityWithCompositeStringIdentifier(); |
||
407 | |||
408 | $emptyStrings->id1 = ''; |
||
409 | $emptyStrings->id2 = ''; |
||
410 | |||
411 | $nonEmptyStrings = new EntityWithCompositeStringIdentifier(); |
||
412 | |||
413 | $nonEmptyStrings->id1 = uniqid('id1', true); |
||
414 | $nonEmptyStrings->id2 = uniqid('id2', true); |
||
415 | |||
416 | $booleanTrue = new EntityWithBooleanIdentifier(); |
||
417 | |||
418 | $booleanTrue->id = true; |
||
419 | |||
420 | $booleanFalse = new EntityWithBooleanIdentifier(); |
||
421 | |||
422 | $booleanFalse->id = false; |
||
423 | |||
424 | return [ |
||
425 | 'empty string, single field' => [$emptyString, ''], |
||
426 | 'non-empty string, single field' => [$nonEmptyString, $nonEmptyString->id], |
||
427 | 'empty strings, two fields' => [$emptyStrings, ' '], |
||
428 | 'non-empty strings, two fields' => [$nonEmptyStrings, $nonEmptyStrings->id1 . ' ' . $nonEmptyStrings->id2], |
||
429 | 'boolean true' => [$booleanTrue, '1'], |
||
430 | 'boolean false' => [$booleanFalse, ''], |
||
431 | ]; |
||
432 | } |
||
433 | |||
434 | public function testRegisteringAManagedInstanceRequiresANonEmptyIdentifier() |
||
435 | { |
||
436 | $this->expectException(ORMInvalidArgumentException::class); |
||
437 | |||
438 | $this->_unitOfWork->registerManaged(new EntityWithBooleanIdentifier(), [], []); |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * @dataProvider entitiesWithInvalidIdentifiersProvider |
||
443 | * |
||
444 | * @param object $entity |
||
445 | * @param array $identifier |
||
446 | * |
||
447 | * @return void |
||
448 | */ |
||
449 | public function testAddToIdentityMapInvalidIdentifiers($entity, array $identifier) |
||
450 | { |
||
451 | $this->expectException(ORMInvalidArgumentException::class); |
||
452 | |||
453 | $this->_unitOfWork->registerManaged($entity, $identifier, []); |
||
454 | } |
||
455 | |||
456 | |||
457 | public function entitiesWithInvalidIdentifiersProvider() |
||
458 | { |
||
459 | $firstNullString = new EntityWithCompositeStringIdentifier(); |
||
460 | |||
461 | $firstNullString->id2 = uniqid('id2', true); |
||
462 | |||
463 | $secondNullString = new EntityWithCompositeStringIdentifier(); |
||
464 | |||
465 | $secondNullString->id1 = uniqid('id1', true); |
||
466 | |||
467 | return [ |
||
468 | 'null string, single field' => [new EntityWithStringIdentifier(), ['id' => null]], |
||
469 | 'null strings, two fields' => [new EntityWithCompositeStringIdentifier(), ['id1' => null, 'id2' => null]], |
||
470 | 'first null string, two fields' => [$firstNullString, ['id1' => null, 'id2' => $firstNullString->id2]], |
||
471 | 'second null string, two fields' => [$secondNullString, ['id1' => $secondNullString->id1, 'id2' => null]], |
||
472 | ]; |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * @group 5689 |
||
477 | * @group 1465 |
||
478 | */ |
||
479 | public function testObjectHashesOfMergedEntitiesAreNotUsedInOriginalEntityDataMap() |
||
480 | { |
||
481 | $user = new CmsUser(); |
||
482 | $user->name = 'ocramius'; |
||
483 | $mergedUser = $this->_unitOfWork->merge($user); |
||
484 | |||
485 | self::assertSame([], $this->_unitOfWork->getOriginalEntityData($user), 'No original data was stored'); |
||
486 | self::assertSame([], $this->_unitOfWork->getOriginalEntityData($mergedUser), 'No original data was stored'); |
||
487 | |||
488 | |||
489 | $user = null; |
||
0 ignored issues
–
show
$user is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
|||
490 | $mergedUser = null; |
||
0 ignored issues
–
show
$mergedUser is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
|||
491 | |||
492 | // force garbage collection of $user (frees the used object hashes, which may be recycled) |
||
493 | gc_collect_cycles(); |
||
494 | |||
495 | $newUser = new CmsUser(); |
||
496 | $newUser->name = 'ocramius'; |
||
497 | |||
498 | $this->_unitOfWork->persist($newUser); |
||
499 | |||
500 | self::assertSame([], $this->_unitOfWork->getOriginalEntityData($newUser), 'No original data was stored'); |
||
501 | } |
||
502 | |||
503 | /** |
||
504 | * @group DDC-1955 |
||
505 | * @group 5570 |
||
506 | * @group 6174 |
||
507 | */ |
||
508 | public function testMergeWithNewEntityWillPersistItAndTriggerPrePersistListenersWithMergedEntityData() |
||
509 | { |
||
510 | $entity = new EntityWithRandomlyGeneratedField(); |
||
511 | |||
512 | $generatedFieldValue = $entity->generatedField; |
||
513 | |||
514 | $this |
||
0 ignored issues
–
show
The method
expects does only exist in PHPUnit_Framework_MockObject_MockObject , but not in Doctrine\Common\EventManager .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
515 | ->eventManager |
||
516 | ->expects(self::any()) |
||
517 | ->method('hasListeners') |
||
518 | ->willReturnCallback(function ($eventName) { |
||
519 | return $eventName === Events::prePersist; |
||
520 | }); |
||
521 | $this |
||
0 ignored issues
–
show
The method
expects does only exist in PHPUnit_Framework_MockObject_MockObject , but not in Doctrine\Common\EventManager .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
522 | ->eventManager |
||
523 | ->expects(self::once()) |
||
524 | ->method('dispatchEvent') |
||
525 | ->with( |
||
526 | self::anything(), |
||
527 | self::callback(function (LifecycleEventArgs $args) use ($entity, $generatedFieldValue) { |
||
528 | /* @var $object EntityWithRandomlyGeneratedField */ |
||
529 | $object = $args->getObject(); |
||
530 | |||
531 | self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); |
||
532 | self::assertNotSame($entity, $object); |
||
533 | self::assertSame($generatedFieldValue, $object->generatedField); |
||
534 | |||
535 | return true; |
||
536 | }) |
||
537 | ); |
||
538 | |||
539 | /* @var $object EntityWithRandomlyGeneratedField */ |
||
540 | $object = $this->_unitOfWork->merge($entity); |
||
541 | |||
542 | self::assertNotSame($object, $entity); |
||
543 | self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); |
||
544 | self::assertSame($object->generatedField, $entity->generatedField); |
||
545 | } |
||
546 | |||
547 | /** |
||
548 | * @group DDC-1955 |
||
549 | * @group 5570 |
||
550 | * @group 6174 |
||
551 | */ |
||
552 | public function testMergeWithExistingEntityWillNotPersistItNorTriggerPrePersistListeners() |
||
553 | { |
||
554 | $persistedEntity = new EntityWithRandomlyGeneratedField(); |
||
555 | $mergedEntity = new EntityWithRandomlyGeneratedField(); |
||
556 | |||
557 | $mergedEntity->id = $persistedEntity->id; |
||
558 | $mergedEntity->generatedField = mt_rand( |
||
559 | $persistedEntity->generatedField + 1, |
||
560 | $persistedEntity->generatedField + 1000 |
||
561 | ); |
||
562 | |||
563 | $this |
||
0 ignored issues
–
show
The method
expects does only exist in PHPUnit_Framework_MockObject_MockObject , but not in Doctrine\Common\EventManager .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
564 | ->eventManager |
||
565 | ->expects(self::any()) |
||
566 | ->method('hasListeners') |
||
567 | ->willReturnCallback(function ($eventName) { |
||
568 | return $eventName === Events::prePersist; |
||
569 | }); |
||
570 | $this->eventManager->expects(self::never())->method('dispatchEvent'); |
||
0 ignored issues
–
show
The method
expects does only exist in PHPUnit_Framework_MockObject_MockObject , but not in Doctrine\Common\EventManager .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
571 | |||
572 | $this->_unitOfWork->registerManaged( |
||
573 | $persistedEntity, |
||
574 | ['id' => $persistedEntity->id], |
||
575 | ['generatedField' => $persistedEntity->generatedField] |
||
576 | ); |
||
577 | |||
578 | /* @var $merged EntityWithRandomlyGeneratedField */ |
||
579 | $merged = $this->_unitOfWork->merge($mergedEntity); |
||
580 | |||
581 | self::assertSame($merged, $persistedEntity); |
||
582 | self::assertSame($persistedEntity->generatedField, $mergedEntity->generatedField); |
||
583 | } |
||
584 | } |
||
585 | |||
586 | /** |
||
587 | * @Entity |
||
588 | */ |
||
589 | class NotifyChangedEntity implements NotifyPropertyChanged |
||
590 | { |
||
591 | private $_listeners = []; |
||
592 | /** |
||
593 | * @Id |
||
594 | * @Column(type="integer") |
||
595 | * @GeneratedValue |
||
596 | */ |
||
597 | private $id; |
||
598 | /** |
||
599 | * @Column(type="string") |
||
600 | */ |
||
601 | private $data; |
||
602 | |||
603 | private $transient; // not persisted |
||
604 | |||
605 | /** @OneToMany(targetEntity="NotifyChangedRelatedItem", mappedBy="owner") */ |
||
606 | private $items; |
||
607 | |||
608 | public function __construct() { |
||
609 | $this->items = new ArrayCollection; |
||
610 | } |
||
611 | |||
612 | public function getId() { |
||
613 | return $this->id; |
||
614 | } |
||
615 | |||
616 | public function getItems() { |
||
617 | return $this->items; |
||
618 | } |
||
619 | |||
620 | public function setTransient($value) { |
||
621 | if ($value != $this->transient) { |
||
622 | $this->_onPropertyChanged('transient', $this->transient, $value); |
||
623 | $this->transient = $value; |
||
624 | } |
||
625 | } |
||
626 | |||
627 | public function getData() { |
||
628 | return $this->data; |
||
629 | } |
||
630 | |||
631 | public function setData($data) { |
||
632 | if ($data != $this->data) { |
||
633 | $this->_onPropertyChanged('data', $this->data, $data); |
||
634 | $this->data = $data; |
||
635 | } |
||
636 | } |
||
637 | |||
638 | public function addPropertyChangedListener(PropertyChangedListener $listener) |
||
639 | { |
||
640 | $this->_listeners[] = $listener; |
||
641 | } |
||
642 | |||
643 | protected function _onPropertyChanged($propName, $oldValue, $newValue) { |
||
644 | if ($this->_listeners) { |
||
0 ignored issues
–
show
The expression
$this->_listeners 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...
|
|||
645 | foreach ($this->_listeners as $listener) { |
||
646 | $listener->propertyChanged($this, $propName, $oldValue, $newValue); |
||
647 | } |
||
648 | } |
||
649 | } |
||
650 | } |
||
651 | |||
652 | /** @Entity */ |
||
653 | class NotifyChangedRelatedItem |
||
654 | { |
||
655 | /** |
||
656 | * @Id |
||
657 | * @Column(type="integer") |
||
658 | * @GeneratedValue |
||
659 | */ |
||
660 | private $id; |
||
661 | |||
662 | /** @ManyToOne(targetEntity="NotifyChangedEntity", inversedBy="items") */ |
||
663 | private $owner; |
||
664 | |||
665 | public function getId() { |
||
666 | return $this->id; |
||
667 | } |
||
668 | |||
669 | public function getOwner() { |
||
670 | return $this->owner; |
||
671 | } |
||
672 | |||
673 | public function setOwner($owner) { |
||
674 | $this->owner = $owner; |
||
675 | } |
||
676 | } |
||
677 | |||
678 | /** @Entity */ |
||
679 | class VersionedAssignedIdentifierEntity |
||
680 | { |
||
681 | /** |
||
682 | * @Id @Column(type="integer") |
||
683 | */ |
||
684 | public $id; |
||
685 | /** |
||
686 | * @Version @Column(type="integer") |
||
687 | */ |
||
688 | public $version; |
||
689 | } |
||
690 | |||
691 | /** @Entity */ |
||
692 | class EntityWithStringIdentifier |
||
693 | { |
||
694 | /** |
||
695 | * @Id @Column(type="string") |
||
696 | * |
||
697 | * @var string|null |
||
698 | */ |
||
699 | public $id; |
||
700 | } |
||
701 | |||
702 | /** @Entity */ |
||
703 | class EntityWithBooleanIdentifier |
||
704 | { |
||
705 | /** |
||
706 | * @Id @Column(type="boolean") |
||
707 | * |
||
708 | * @var bool|null |
||
709 | */ |
||
710 | public $id; |
||
711 | } |
||
712 | |||
713 | /** @Entity */ |
||
714 | class EntityWithCompositeStringIdentifier |
||
715 | { |
||
716 | /** |
||
717 | * @Id @Column(type="string") |
||
718 | * |
||
719 | * @var string|null |
||
720 | */ |
||
721 | public $id1; |
||
722 | |||
723 | /** |
||
724 | * @Id @Column(type="string") |
||
725 | * |
||
726 | * @var string|null |
||
727 | */ |
||
728 | public $id2; |
||
729 | } |
||
730 | |||
731 | /** @Entity */ |
||
732 | class EntityWithRandomlyGeneratedField |
||
733 | { |
||
734 | /** @Id @Column(type="string") */ |
||
735 | public $id; |
||
736 | |||
737 | /** |
||
738 | * @Column(type="integer") |
||
739 | */ |
||
740 | public $generatedField; |
||
741 | |||
742 | public function __construct() |
||
743 | { |
||
744 | $this->id = uniqid('id', true); |
||
745 | $this->generatedField = mt_rand(0, 100000); |
||
746 | } |
||
747 | } |
||
748 |
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.