Failed Conditions
Pull Request — master (#7085)
by Guilherme
14:34
created

Cache/Persister/Entity/AbstractEntityPersister.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Cache\Persister\Entity;
6
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\ORM\Cache;
9
use Doctrine\ORM\Cache\CollectionCacheKey;
10
use Doctrine\ORM\Cache\EntityCacheKey;
11
use Doctrine\ORM\Cache\EntityHydrator;
12
use Doctrine\ORM\Cache\Logging\CacheLogger;
13
use Doctrine\ORM\Cache\Persister\CachedPersister;
14
use Doctrine\ORM\Cache\QueryCacheKey;
15
use Doctrine\ORM\Cache\Region;
16
use Doctrine\ORM\Cache\TimestampCacheKey;
17
use Doctrine\ORM\Cache\TimestampRegion;
18
use Doctrine\ORM\EntityManagerInterface;
19
use Doctrine\ORM\Mapping\AssociationMetadata;
20
use Doctrine\ORM\Mapping\ClassMetadata;
21
use Doctrine\ORM\Mapping\ClassMetadataFactory;
22
use Doctrine\ORM\Mapping\FetchMode;
23
use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata;
24
use Doctrine\ORM\Mapping\OneToManyAssociationMetadata;
25
use Doctrine\ORM\Mapping\ToOneAssociationMetadata;
26
use Doctrine\ORM\PersistentCollection;
27
use Doctrine\ORM\Persisters\Entity\EntityPersister;
28
use Doctrine\ORM\Utility\StaticClassNameConverter;
29
use function serialize;
30
use function sha1;
31
32
abstract class AbstractEntityPersister implements CachedEntityPersister
33
{
34
    /**
35
     * @var EntityManagerInterface
36
     */
37
    protected $em;
38
39
    /**
40
     * @var ClassMetadataFactory
41
     */
42
    protected $metadataFactory;
43
44
    /**
45
     * @var EntityPersister
46
     */
47
    protected $persister;
48
49
    /**
50
     * @var ClassMetadata
51
     */
52
    protected $class;
53
54
    /**
55
     * @var mixed[][]
56
     */
57
    protected $queuedCache = [];
58
59
    /**
60
     * @var Region
61
     */
62
    protected $region;
63
64
    /**
65
     * @var TimestampRegion
66
     */
67
    protected $timestampRegion;
68
69
    /**
70
     * @var TimestampCacheKey
71
     */
72
    protected $timestampKey;
73
74
    /**
75
     * @var EntityHydrator
76
     */
77
    protected $hydrator;
78
79
    /**
80
     * @var Cache
81
     */
82
    protected $cache;
83
84
    /**
85
     * @var CacheLogger
86
     */
87
    protected $cacheLogger;
88
89
    /**
90
     * @var string
91
     */
92
    protected $regionName;
93
94
    /**
95
     * Associations configured as FetchMode::EAGER, as well as all inverse one-to-one associations.
96
     *
97
     * @var string[]|null
98
     */
99
    protected $joinedAssociations;
100
101
    /**
102
     * @param EntityPersister        $persister The entity persister to cache.
103
     * @param Region                 $region    The entity cache region.
104
     * @param EntityManagerInterface $em        The entity manager.
105
     * @param ClassMetadata          $class     The entity metadata.
106
     */
107 203
    public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class)
108
    {
109 203
        $configuration = $em->getConfiguration();
110 203
        $cacheConfig   = $configuration->getSecondLevelCacheConfiguration();
111 203
        $cacheFactory  = $cacheConfig->getCacheFactory();
112
113 203
        $this->em              = $em;
114 203
        $this->class           = $class;
115 203
        $this->region          = $region;
116 203
        $this->persister       = $persister;
117 203
        $this->cache           = $em->getCache();
118 203
        $this->regionName      = $region->getName();
119 203
        $this->metadataFactory = $em->getMetadataFactory();
120 203
        $this->cacheLogger     = $cacheConfig->getCacheLogger();
121 203
        $this->timestampRegion = $cacheFactory->getTimestampRegion();
122 203
        $this->hydrator        = $cacheFactory->buildEntityHydrator($em, $class);
123 203
        $this->timestampKey    = new TimestampCacheKey($this->class->getRootClassName());
124 203
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 3
    public function getSelectSQL(
130
        $criteria,
131
        ?AssociationMetadata $association = null,
132
        $lockMode = null,
133
        $limit = null,
134
        $offset = null,
135
        array $orderBy = []
136
    ) {
137 3
        return $this->persister->getSelectSQL($criteria, $association, $lockMode, $limit, $offset, $orderBy);
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143
    public function getCountSQL($criteria = [])
144
    {
145
        return $this->persister->getCountSQL($criteria);
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 9
    public function getInsertSQL()
152
    {
153 9
        return $this->persister->getInsertSQL();
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 17
    public function getResultSetMapping()
160
    {
161 17
        return $this->persister->getResultSetMapping();
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 3
    public function getSelectConditionStatementSQL(
168
        $field,
169
        $value,
170
        ?AssociationMetadata $association = null,
171
        $comparison = null
172
    ) {
173 3
        return $this->persister->getSelectConditionStatementSQL($field, $value, $association, $comparison);
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 4
    public function exists($entity, ?Criteria $extraConditions = null)
180
    {
181 4
        if ($extraConditions === null) {
182 4
            $key = new EntityCacheKey($this->class->getRootClassName(), $this->getIdentifier($entity));
183
184 4
            if ($this->region->contains($key)) {
185 1
                return true;
186
            }
187
        }
188
189 4
        return $this->persister->exists($entity, $extraConditions);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 125
    public function getCacheRegion()
196
    {
197 125
        return $this->region;
198
    }
199
200
    /**
201
     * @return EntityHydrator
202
     */
203 45
    public function getEntityHydrator()
204
    {
205 45
        return $this->hydrator;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 38
    public function storeEntityCache($entity, EntityCacheKey $key)
212
    {
213 38
        $class     = $this->class;
214 38
        $className = StaticClassNameConverter::getClass($entity);
215
216 38
        if ($className !== $this->class->getClassName()) {
217 3
            $class = $this->metadataFactory->getMetadataFor($className);
218
        }
219
220 38
        $entry  = $this->hydrator->buildCacheEntry($class, $key, $entity);
221 38
        $cached = $this->region->put($key, $entry);
222
223 38
        if ($this->cacheLogger && $cached) {
224 24
            $this->cacheLogger->entityCachePut($this->regionName, $key);
225
        }
226
227 38
        return $cached;
228
    }
229
230
    /**
231
     * @param object $entity
232
     */
233 29
    private function storeJoinedAssociations($entity)
234
    {
235 29
        if ($this->joinedAssociations === null) {
236 29
            $associations = [];
237
238 29
            foreach ($this->class->getDeclaredPropertiesIterator() as $association) {
239 29
                if ($association instanceof ToOneAssociationMetadata &&
240 29
                    $association->getCache() &&
241 29
                    ($association->getFetchMode() === FetchMode::EAGER || ! $association->isOwningSide())) {
242 29
                    $associations[] = $association->getName();
243
                }
244
            }
245
246 29
            $this->joinedAssociations = $associations;
247
        }
248
249 29
        $uow = $this->em->getUnitOfWork();
250
251 29
        foreach ($this->joinedAssociations as $name) {
252 2
            $association  = $this->class->getProperty($name);
253 2
            $assocEntity  = $association->getValue($entity);
254 2
            $targetEntity = $association->getTargetEntity();
255
256 2
            if ($assocEntity === null) {
257
                continue;
258
            }
259
260 2
            $assocId        = $uow->getEntityIdentifier($assocEntity);
261 2
            $assocMetadata  = $this->metadataFactory->getMetadataFor($targetEntity);
262 2
            $assocKey       = new EntityCacheKey($assocMetadata->getRootClassName(), $assocId);
263 2
            $assocPersister = $uow->getEntityPersister($targetEntity);
264
265 2
            $assocPersister->storeEntityCache($assocEntity, $assocKey);
266
        }
267 29
    }
268
269
    /**
270
     * Generates a string of currently query
271
     *
272
     * @param string  $query
273
     * @param string  $criteria
274
     * @param mixed[] $orderBy
275
     * @param int     $limit
276
     * @param int     $offset
277
     *
278
     * @return string
279
     */
280 17
    protected function getHash($query, $criteria, ?array $orderBy = null, $limit = null, $offset = null)
281
    {
282 17
        list($params) = ($criteria instanceof Criteria)
0 ignored issues
show
The condition $criteria instanceof Doc...on\Collections\Criteria can never be true since $criteria is never a sub-type of Doctrine\Common\Collections\Criteria.
Loading history...
283 6
            ? $this->persister->expandCriteriaParameters($criteria)
284 17
            : $this->persister->expandParameters($criteria);
285
286 17
        return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
287
    }
288
289
    /**
290
     * {@inheritdoc}
291
     */
292 3
    public function expandParameters($criteria)
293
    {
294 3
        return $this->persister->expandParameters($criteria);
295
    }
296
297
    /**
298
     * {@inheritdoc}
299
     */
300 3
    public function expandCriteriaParameters(Criteria $criteria)
301
    {
302 3
        return $this->persister->expandCriteriaParameters($criteria);
303
    }
304
305
    /**
306
     * {@inheritdoc}
307
     */
308 14
    public function getClassMetadata()
309
    {
310 14
        return $this->persister->getClassMetadata();
311
    }
312
313
    /**
314
     * {@inheritdoc}
315
     */
316 3
    public function getManyToManyCollection(
317
        ManyToManyAssociationMetadata $association,
318
        $sourceEntity,
319
        $offset = null,
320
        $limit = null
321
    ) {
322 3
        return $this->persister->getManyToManyCollection($association, $sourceEntity, $offset, $limit);
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328 3
    public function getOneToManyCollection(
329
        OneToManyAssociationMetadata $association,
330
        $sourceEntity,
331
        $offset = null,
332
        $limit = null
333
    ) {
334 3
        return $this->persister->getOneToManyCollection($association, $sourceEntity, $offset, $limit);
335
    }
336
337
    /**
338
     * {@inheritdoc}
339
     */
340 3
    public function getOwningTable($fieldName)
341
    {
342 3
        return $this->persister->getOwningTable($fieldName);
343
    }
344
345
    /**
346
     * {@inheritdoc}
347
     */
348 117
    public function getIdentifier($entity) : array
349
    {
350 117
        return $this->persister->getIdentifier($entity);
351
    }
352
353
    /**
354
     * {@inheritdoc}
355
     */
356 45
    public function setIdentifier($entity, array $id) : void
357
    {
358 45
        $this->persister->setIdentifier($entity, $id);
359 45
    }
360
361
    /**
362
     * {@inheritdoc}
363
     */
364 59
    public function getColumnValue($entity, string $columnName)
365
    {
366 59
        return $this->persister->getColumnValue($entity, $columnName);
367
    }
368
369
    /**
370
     * {@inheritdoc}
371
     */
372 99
    public function insert($entity)
373
    {
374 99
        $this->queuedCache['insert'][] = $entity;
375
376 99
        $this->persister->insert($entity);
377 99
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382 6
    public function load(
383
        array $criteria,
384
        $entity = null,
385
        ?AssociationMetadata $association = null,
386
        array $hints = [],
387
        $lockMode = null,
388
        $limit = null,
389
        ?array $orderBy = null
390
    ) {
391 6
        if ($entity !== null || $association !== null || ! empty($hints) || $lockMode !== null) {
392 3
            return $this->persister->load($criteria, $entity, $association, $hints, $lockMode, $limit, $orderBy);
393
        }
394
395
        //handle only EntityRepository#findOneBy
396 3
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
397 3
        $hash       = $this->getHash($query, $criteria, null, null, null);
398 3
        $rsm        = $this->getResultSetMapping();
399 3
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
400 3
        $queryCache = $this->cache->getQueryCache($this->regionName);
401 3
        $result     = $queryCache->get($queryKey, $rsm);
402
403 3
        if ($result !== null) {
404 3
            if ($this->cacheLogger) {
405 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
406
            }
407
408 3
            return $result[0];
409
        }
410
411 3
        $result = $this->persister->load($criteria, $entity, $association, $hints, $lockMode, $limit, $orderBy);
412
413 3
        if ($result === null) {
414
            return null;
415
        }
416
417 3
        $cached = $queryCache->put($queryKey, $rsm, [$result]);
418
419 3
        if ($this->cacheLogger) {
420 3
            if ($result) {
0 ignored issues
show
The condition $result can never be true.
Loading history...
421 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
422
            }
423
424 3
            if ($cached) {
425 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
426
            }
427
        }
428
429 3
        return $result;
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435 8
    public function loadAll(array $criteria = [], array $orderBy = [], $limit = null, $offset = null)
436
    {
437 8
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
438 8
        $hash       = $this->getHash($query, $criteria, null, null, null);
439 8
        $rsm        = $this->getResultSetMapping();
440 8
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
441 8
        $queryCache = $this->cache->getQueryCache($this->regionName);
442 8
        $result     = $queryCache->get($queryKey, $rsm);
443
444 8
        if ($result !== null) {
445 5
            if ($this->cacheLogger) {
446 5
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
447
            }
448
449 5
            return $result;
450
        }
451
452 8
        $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
453 8
        $cached = $queryCache->put($queryKey, $rsm, $result);
454
455 8
        if ($this->cacheLogger) {
456 5
            if ($result) {
457 5
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
458
            }
459
460 5
            if ($cached) {
461 5
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
462
            }
463
        }
464
465 8
        return $result;
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471 48
    public function loadById(array $identifier, $entity = null)
472
    {
473 48
        $cacheKey   = new EntityCacheKey($this->class->getRootClassName(), $identifier);
474 48
        $cacheEntry = $this->region->get($cacheKey);
475 48
        $class      = $this->class;
476
477 48
        if ($cacheEntry !== null) {
478 40
            if ($cacheEntry->class !== $this->class->getClassName()) {
479 3
                $class = $this->metadataFactory->getMetadataFor($cacheEntry->class);
480
            }
481
482 40
            $entity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity);
483
484 40
            if ($entity !== null) {
485 40
                if ($this->cacheLogger) {
486 40
                    $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey);
487
                }
488
489 40
                return $entity;
490
            }
491
        }
492
493 33
        $entity = $this->persister->loadById($identifier, $entity);
494
495 33
        if ($entity === null) {
496 2
            return null;
497
        }
498
499 33
        $class     = $this->class;
500 33
        $className = StaticClassNameConverter::getClass($entity);
501
502 33
        if ($className !== $this->class->getClassName()) {
503 4
            $class = $this->metadataFactory->getMetadataFor($className);
504
        }
505
506 33
        $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);
507 33
        $cached     = $this->region->put($cacheKey, $cacheEntry);
508
509 33
        if ($cached && ($this->joinedAssociations === null || $this->joinedAssociations)) {
510 29
            $this->storeJoinedAssociations($entity);
511
        }
512
513 33
        if ($this->cacheLogger) {
514 30
            if ($cached) {
515 29
                $this->cacheLogger->entityCachePut($this->regionName, $cacheKey);
516
            }
517
518 30
            $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey);
519
        }
520
521 33
        return $entity;
522
    }
523
524
    /**
525
     * {@inheritDoc}
526
     */
527 2
    public function count($criteria = [])
528
    {
529 2
        return $this->persister->count($criteria);
530
    }
531
532
    /**
533
     * {@inheritdoc}
534
     */
535 6
    public function loadCriteria(Criteria $criteria)
536
    {
537 6
        $orderBy     = $criteria->getOrderings();
538 6
        $limit       = $criteria->getMaxResults();
539 6
        $offset      = $criteria->getFirstResult();
540 6
        $query       = $this->persister->getSelectSQL($criteria);
541 6
        $hash        = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
542 6
        $rsm         = $this->getResultSetMapping();
543 6
        $queryKey    = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
544 6
        $queryCache  = $this->cache->getQueryCache($this->regionName);
545 6
        $cacheResult = $queryCache->get($queryKey, $rsm);
546
547 6
        if ($cacheResult !== null) {
548 3
            if ($this->cacheLogger) {
549 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
550
            }
551
552 3
            return $cacheResult;
553
        }
554
555 6
        $result = $this->persister->loadCriteria($criteria);
556 6
        $cached = $queryCache->put($queryKey, $rsm, $result);
557
558 6
        if ($this->cacheLogger) {
559 3
            if ($result) {
560 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
561
            }
562
563 3
            if ($cached) {
564 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
565
            }
566
        }
567
568 6
        return $result;
569
    }
570
571
    /**
572
     * {@inheritdoc}
573
     */
574 7
    public function loadManyToManyCollection(
575
        ManyToManyAssociationMetadata $association,
576
        $sourceEntity,
577
        PersistentCollection $collection
578
    ) {
579 7
        $uow       = $this->em->getUnitOfWork();
580 7
        $persister = $uow->getCollectionPersister($association);
581 7
        $hasCache  = ($persister instanceof CachedPersister);
582 7
        $key       = null;
583
584 7
        if (! $hasCache) {
585 3
            return $this->persister->loadManyToManyCollection($association, $sourceEntity, $collection);
586
        }
587
588 4
        $ownerId = $uow->getEntityIdentifier($collection->getOwner());
589 4
        $key     = $this->buildCollectionCacheKey($association, $ownerId);
590 4
        $list    = $persister->loadCollectionCache($collection, $key);
591
592 4
        if ($list !== null) {
593 4
            if ($this->cacheLogger) {
594 4
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
595
            }
596
597 4
            return $list;
598
        }
599
600 2
        $list = $this->persister->loadManyToManyCollection($association, $sourceEntity, $collection);
601
602 2
        $persister->storeCollectionCache($key, $list);
603
604 2
        if ($this->cacheLogger) {
605 2
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
606
        }
607
608 2
        return $list;
609
    }
610
611
    /**
612
     * {@inheritdoc}
613
     */
614 17
    public function loadOneToManyCollection(
615
        OneToManyAssociationMetadata $association,
616
        $sourceEntity,
617
        PersistentCollection $collection
618
    ) {
619 17
        $uow       = $this->em->getUnitOfWork();
620 17
        $persister = $uow->getCollectionPersister($association);
621 17
        $hasCache  = ($persister instanceof CachedPersister);
622
623 17
        if (! $hasCache) {
624 3
            return $this->persister->loadOneToManyCollection($association, $sourceEntity, $collection);
625
        }
626
627 14
        $ownerId = $uow->getEntityIdentifier($collection->getOwner());
628 14
        $key     = $this->buildCollectionCacheKey($association, $ownerId);
629 14
        $list    = $persister->loadCollectionCache($collection, $key);
630
631 14
        if ($list !== null) {
632 11
            if ($this->cacheLogger) {
633 11
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
634
            }
635
636 11
            return $list;
637
        }
638
639 13
        $list = $this->persister->loadOneToManyCollection($association, $sourceEntity, $collection);
640
641 13
        $persister->storeCollectionCache($key, $list);
642
643 13
        if ($this->cacheLogger) {
644 13
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
645
        }
646
647 13
        return $list;
648
    }
649
650
    /**
651
     * {@inheritdoc}
652
     */
653 5
    public function loadToOneEntity(ToOneAssociationMetadata $association, $sourceEntity, array $identifier = [])
654
    {
655 5
        return $this->persister->loadToOneEntity($association, $sourceEntity, $identifier);
656
    }
657
658
    /**
659
     * {@inheritdoc}
660
     */
661 3
    public function lock(array $criteria, $lockMode)
662
    {
663 3
        $this->persister->lock($criteria, $lockMode);
664 3
    }
665
666
    /**
667
     * {@inheritdoc}
668
     */
669 3
    public function refresh(array $id, $entity, $lockMode = null)
670
    {
671 3
        $this->persister->refresh($id, $entity, $lockMode);
672 3
    }
673
674
    /**
675
     * @param mixed[] $ownerId
676
     *
677
     * @return CollectionCacheKey
678
     */
679 18
    protected function buildCollectionCacheKey(AssociationMetadata $association, $ownerId)
680
    {
681
        /** @var ClassMetadata $metadata */
682 18
        $metadata = $this->metadataFactory->getMetadataFor($association->getSourceEntity());
683
684 18
        return new CollectionCacheKey($metadata->getRootClassName(), $association->getName(), $ownerId);
685
    }
686
}
687