Completed
Push — master ( a0071b...e33605 )
by Michael
12s
created

Cache/Persister/Entity/AbstractEntityPersister.php (1 issue)

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\Persister\CachedPersister;
12
use Doctrine\ORM\Cache\QueryCacheKey;
13
use Doctrine\ORM\Cache\Region;
14
use Doctrine\ORM\Cache\TimestampCacheKey;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Doctrine\ORM\Mapping\AssociationMetadata;
17
use Doctrine\ORM\Mapping\ClassMetadata;
18
use Doctrine\ORM\Mapping\FetchMode;
19
use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata;
20
use Doctrine\ORM\Mapping\OneToManyAssociationMetadata;
21
use Doctrine\ORM\Mapping\ToOneAssociationMetadata;
22
use Doctrine\ORM\PersistentCollection;
23
use Doctrine\ORM\Persisters\Entity\EntityPersister;
24
use Doctrine\ORM\Utility\StaticClassNameConverter;
25
26
abstract class AbstractEntityPersister implements CachedEntityPersister
27
{
28
    /**
29
     * @var \Doctrine\ORM\EntityManagerInterface
30
     */
31
    protected $em;
32
33
    /**
34
     * @var \Doctrine\ORM\Mapping\ClassMetadataFactory
35
     */
36
    protected $metadataFactory;
37
38
    /**
39
     * @var \Doctrine\ORM\Persisters\Entity\EntityPersister
40
     */
41
    protected $persister;
42
43
    /**
44
     * @var \Doctrine\ORM\Mapping\ClassMetadata
45
     */
46
    protected $class;
47
48
    /**
49
     * @var object[][][]
50
     */
51
    protected $queuedCache = [];
52
53
    /**
54
     * @var \Doctrine\ORM\Cache\Region
55
     */
56
    protected $region;
57
58
    /**
59
     * @var \Doctrine\ORM\Cache\TimestampRegion
60
     */
61
    protected $timestampRegion;
62
63
    /**
64
     * @var \Doctrine\ORM\Cache\TimestampCacheKey
65
     */
66
    protected $timestampKey;
67
68
    /**
69
     * @var \Doctrine\ORM\Cache\EntityHydrator
70
     */
71
    protected $hydrator;
72
73
    /**
74
     * @var \Doctrine\ORM\Cache
75
     */
76
    protected $cache;
77
78
    /**
79
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger
80
     */
81
    protected $cacheLogger;
82
83
    /**
84
     * @var string
85
     */
86
    protected $regionName;
87
88
    /**
89
     * Associations configured as FetchMode::EAGER, as well as all inverse one-to-one associations.
90
     *
91
     * @var string[]|null
92
     */
93
    protected $joinedAssociations;
94
95
    /**
96
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache.
97
     * @param \Doctrine\ORM\Cache\Region                      $region    The entity cache region.
98
     * @param \Doctrine\ORM\EntityManagerInterface            $em        The entity manager.
99
     * @param \Doctrine\ORM\Mapping\ClassMetadata             $class     The entity metadata.
100
     */
101 202
    public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class)
102
    {
103 202
        $configuration = $em->getConfiguration();
104 202
        $cacheConfig   = $configuration->getSecondLevelCacheConfiguration();
105 202
        $cacheFactory  = $cacheConfig->getCacheFactory();
106
107 202
        $this->em              = $em;
108 202
        $this->class           = $class;
109 202
        $this->region          = $region;
110 202
        $this->persister       = $persister;
111 202
        $this->cache           = $em->getCache();
112 202
        $this->regionName      = $region->getName();
113 202
        $this->metadataFactory = $em->getMetadataFactory();
114 202
        $this->cacheLogger     = $cacheConfig->getCacheLogger();
115 202
        $this->timestampRegion = $cacheFactory->getTimestampRegion();
116 202
        $this->hydrator        = $cacheFactory->buildEntityHydrator($em, $class);
117 202
        $this->timestampKey    = new TimestampCacheKey($this->class->getRootClassName());
118 202
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123 3
    public function getSelectSQL(
124
        $criteria,
125
        ?AssociationMetadata $association = null,
126
        $lockMode = null,
127
        $limit = null,
128
        $offset = null,
129
        array $orderBy = []
130
    ) {
131 3
        return $this->persister->getSelectSQL($criteria, $association, $lockMode, $limit, $offset, $orderBy);
132
    }
133
134
    /**
135
     * {@inheritDoc}
136
     */
137
    public function getCountSQL($criteria = [])
138
    {
139
        return $this->persister->getCountSQL($criteria);
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 9
    public function getInsertSQL()
146
    {
147 9
        return $this->persister->getInsertSQL();
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 17
    public function getResultSetMapping()
154
    {
155 17
        return $this->persister->getResultSetMapping();
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 3
    public function getSelectConditionStatementSQL(
162
        $field,
163
        $value,
164
        ?AssociationMetadata $association = null,
165
        $comparison = null
166
    ) {
167 3
        return $this->persister->getSelectConditionStatementSQL($field, $value, $association, $comparison);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173 4
    public function exists($entity, ?Criteria $extraConditions = null)
174
    {
175 4
        if ($extraConditions === null) {
176 4
            $key = new EntityCacheKey($this->class->getRootClassName(), $this->getIdentifier($entity));
177
178 4
            if ($this->region->contains($key)) {
179 1
                return true;
180
            }
181
        }
182
183 4
        return $this->persister->exists($entity, $extraConditions);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 125
    public function getCacheRegion()
190
    {
191 125
        return $this->region;
192
    }
193
194
    /**
195
     * @return \Doctrine\ORM\Cache\EntityHydrator
196
     */
197 45
    public function getEntityHydrator()
198
    {
199 45
        return $this->hydrator;
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205 38
    public function storeEntityCache($entity, EntityCacheKey $key)
206
    {
207 38
        $class     = $this->class;
208 38
        $className = StaticClassNameConverter::getClass($entity);
209
210 38
        if ($className !== $this->class->getClassName()) {
211 3
            $class = $this->metadataFactory->getMetadataFor($className);
212
        }
213
214 38
        $entry  = $this->hydrator->buildCacheEntry($class, $key, $entity);
215 38
        $cached = $this->region->put($key, $entry);
216
217 38
        if ($this->cacheLogger && $cached) {
218 24
            $this->cacheLogger->entityCachePut($this->regionName, $key);
219
        }
220
221 38
        return $cached;
222
    }
223
224
    /**
225
     * @param object $entity
226
     */
227 29
    private function storeJoinedAssociations($entity)
228
    {
229 29
        if ($this->joinedAssociations === null) {
230 29
            $associations = [];
231
232 29
            foreach ($this->class->getDeclaredPropertiesIterator() as $association) {
233 29
                if ($association instanceof ToOneAssociationMetadata &&
234 29
                    $association->getCache() &&
235 29
                    ($association->getFetchMode() === FetchMode::EAGER || ! $association->isOwningSide())) {
236 29
                    $associations[] = $association->getName();
237
                }
238
            }
239
240 29
            $this->joinedAssociations = $associations;
241
        }
242
243 29
        $uow = $this->em->getUnitOfWork();
244
245 29
        foreach ($this->joinedAssociations as $name) {
246 2
            $association  = $this->class->getProperty($name);
247 2
            $assocEntity  = $association->getValue($entity);
248 2
            $targetEntity = $association->getTargetEntity();
249
250 2
            if ($assocEntity === null) {
251
                continue;
252
            }
253
254 2
            $assocId        = $uow->getEntityIdentifier($assocEntity);
255 2
            $assocMetadata  = $this->metadataFactory->getMetadataFor($targetEntity);
256 2
            $assocKey       = new EntityCacheKey($assocMetadata->getRootClassName(), $assocId);
257 2
            $assocPersister = $uow->getEntityPersister($targetEntity);
258
259 2
            $assocPersister->storeEntityCache($assocEntity, $assocKey);
0 ignored issues
show
The method storeEntityCache() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. It seems like you code against a sub-type of Doctrine\ORM\Persisters\Entity\EntityPersister such as Doctrine\ORM\Cache\Persi...y\CachedEntityPersister. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

259
            $assocPersister->/** @scrutinizer ignore-call */ 
260
                             storeEntityCache($assocEntity, $assocKey);
Loading history...
260
        }
261 29
    }
262
263
    /**
264
     * Generates a string of currently query
265
     *
266
     * @param string  $query
267
     * @param string  $criteria
268
     * @param mixed[] $orderBy
269
     * @param int     $limit
270
     * @param int     $offset
271
     *
272
     * @return string
273
     */
274 17
    protected function getHash($query, $criteria, ?array $orderBy = null, $limit = null, $offset = null)
275
    {
276 17
        list($params) = ($criteria instanceof Criteria)
277 6
            ? $this->persister->expandCriteriaParameters($criteria)
278 17
            : $this->persister->expandParameters($criteria);
279
280 17
        return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286 3
    public function expandParameters($criteria)
287
    {
288 3
        return $this->persister->expandParameters($criteria);
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294 3
    public function expandCriteriaParameters(Criteria $criteria)
295
    {
296 3
        return $this->persister->expandCriteriaParameters($criteria);
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302 14
    public function getClassMetadata()
303
    {
304 14
        return $this->persister->getClassMetadata();
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310 3
    public function getManyToManyCollection(
311
        ManyToManyAssociationMetadata $association,
312
        $sourceEntity,
313
        $offset = null,
314
        $limit = null
315
    ) {
316 3
        return $this->persister->getManyToManyCollection($association, $sourceEntity, $offset, $limit);
317
    }
318
319
    /**
320
     * {@inheritdoc}
321
     */
322 3
    public function getOneToManyCollection(
323
        OneToManyAssociationMetadata $association,
324
        $sourceEntity,
325
        $offset = null,
326
        $limit = null
327
    ) {
328 3
        return $this->persister->getOneToManyCollection($association, $sourceEntity, $offset, $limit);
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334 3
    public function getOwningTable($fieldName)
335
    {
336 3
        return $this->persister->getOwningTable($fieldName);
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342 116
    public function getIdentifier($entity) : array
343
    {
344 116
        return $this->persister->getIdentifier($entity);
345
    }
346
347
    /**
348
     * {@inheritdoc}
349
     */
350 45
    public function setIdentifier($entity, array $id) : void
351
    {
352 45
        $this->persister->setIdentifier($entity, $id);
353 45
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358 59
    public function getColumnValue($entity, string $columnName)
359
    {
360 59
        return $this->persister->getColumnValue($entity, $columnName);
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366 98
    public function insert($entity)
367
    {
368 98
        $this->queuedCache['insert'][] = $entity;
369
370 98
        $this->persister->insert($entity);
371 98
    }
372
373
    /**
374
     * {@inheritdoc}
375
     */
376 6
    public function load(
377
        array $criteria,
378
        $entity = null,
379
        ?AssociationMetadata $association = null,
380
        array $hints = [],
381
        $lockMode = null,
382
        $limit = null,
383
        ?array $orderBy = null
384
    ) {
385 6
        if ($entity !== null || $association !== null || ! empty($hints) || $lockMode !== null) {
386 3
            return $this->persister->load($criteria, $entity, $association, $hints, $lockMode, $limit, $orderBy);
387
        }
388
389
        //handle only EntityRepository#findOneBy
390 3
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
391 3
        $hash       = $this->getHash($query, $criteria, null, null, null);
392 3
        $rsm        = $this->getResultSetMapping();
393 3
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
394 3
        $queryCache = $this->cache->getQueryCache($this->regionName);
395 3
        $result     = $queryCache->get($queryKey, $rsm);
396
397 3
        if ($result !== null) {
398 3
            if ($this->cacheLogger) {
399 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
400
            }
401
402 3
            return $result[0];
403
        }
404
405 3
        $result = $this->persister->load($criteria, $entity, $association, $hints, $lockMode, $limit, $orderBy);
406
407 3
        if ($result === null) {
408
            return null;
409
        }
410
411 3
        $cached = $queryCache->put($queryKey, $rsm, [$result]);
412
413 3
        if ($this->cacheLogger) {
414 3
            if ($result) {
415 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
416
            }
417
418 3
            if ($cached) {
419 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
420
            }
421
        }
422
423 3
        return $result;
424
    }
425
426
    /**
427
     * {@inheritdoc}
428
     */
429 8
    public function loadAll(array $criteria = [], array $orderBy = [], $limit = null, $offset = null)
430
    {
431 8
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
432 8
        $hash       = $this->getHash($query, $criteria, null, null, null);
433 8
        $rsm        = $this->getResultSetMapping();
434 8
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
435 8
        $queryCache = $this->cache->getQueryCache($this->regionName);
436 8
        $result     = $queryCache->get($queryKey, $rsm);
437
438 8
        if ($result !== null) {
439 5
            if ($this->cacheLogger) {
440 5
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
441
            }
442
443 5
            return $result;
444
        }
445
446 8
        $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
447 8
        $cached = $queryCache->put($queryKey, $rsm, $result);
448
449 8
        if ($this->cacheLogger) {
450 5
            if ($result) {
451 5
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
452
            }
453
454 5
            if ($cached) {
455 5
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
456
            }
457
        }
458
459 8
        return $result;
460
    }
461
462
    /**
463
     * {@inheritdoc}
464
     */
465 47
    public function loadById(array $identifier, $entity = null)
466
    {
467 47
        $cacheKey   = new EntityCacheKey($this->class->getRootClassName(), $identifier);
468 47
        $cacheEntry = $this->region->get($cacheKey);
469 47
        $class      = $this->class;
470
471 47
        if ($cacheEntry !== null) {
472 39
            if ($cacheEntry->class !== $this->class->getClassName()) {
473 3
                $class = $this->metadataFactory->getMetadataFor($cacheEntry->class);
474
            }
475
476 39
            $entity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity);
477
478 39
            if ($entity !== null) {
479 39
                if ($this->cacheLogger) {
480 39
                    $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey);
481
                }
482
483 39
                return $entity;
484
            }
485
        }
486
487 33
        $entity = $this->persister->loadById($identifier, $entity);
488
489 33
        if ($entity === null) {
490 2
            return null;
491
        }
492
493 33
        $class     = $this->class;
494 33
        $className = StaticClassNameConverter::getClass($entity);
495
496 33
        if ($className !== $this->class->getClassName()) {
497 4
            $class = $this->metadataFactory->getMetadataFor($className);
498
        }
499
500 33
        $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);
501 33
        $cached     = $this->region->put($cacheKey, $cacheEntry);
502
503 33
        if ($cached && ($this->joinedAssociations === null || $this->joinedAssociations)) {
504 29
            $this->storeJoinedAssociations($entity);
505
        }
506
507 33
        if ($this->cacheLogger) {
508 30
            if ($cached) {
509 29
                $this->cacheLogger->entityCachePut($this->regionName, $cacheKey);
510
            }
511
512 30
            $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey);
513
        }
514
515 33
        return $entity;
516
    }
517
518
    /**
519
     * {@inheritDoc}
520
     */
521 2
    public function count($criteria = [])
522
    {
523 2
        return $this->persister->count($criteria);
524
    }
525
526
    /**
527
     * {@inheritdoc}
528
     */
529 6
    public function loadCriteria(Criteria $criteria)
530
    {
531 6
        $orderBy     = $criteria->getOrderings();
532 6
        $limit       = $criteria->getMaxResults();
533 6
        $offset      = $criteria->getFirstResult();
534 6
        $query       = $this->persister->getSelectSQL($criteria);
535 6
        $hash        = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
536 6
        $rsm         = $this->getResultSetMapping();
537 6
        $queryKey    = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
538 6
        $queryCache  = $this->cache->getQueryCache($this->regionName);
539 6
        $cacheResult = $queryCache->get($queryKey, $rsm);
540
541 6
        if ($cacheResult !== null) {
542 3
            if ($this->cacheLogger) {
543 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
544
            }
545
546 3
            return $cacheResult;
547
        }
548
549 6
        $result = $this->persister->loadCriteria($criteria);
550 6
        $cached = $queryCache->put($queryKey, $rsm, $result);
551
552 6
        if ($this->cacheLogger) {
553 3
            if ($result) {
554 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
555
            }
556
557 3
            if ($cached) {
558 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
559
            }
560
        }
561
562 6
        return $result;
563
    }
564
565
    /**
566
     * {@inheritdoc}
567
     */
568 7
    public function loadManyToManyCollection(
569
        ManyToManyAssociationMetadata $association,
570
        $sourceEntity,
571
        PersistentCollection $collection
572
    ) {
573 7
        $uow       = $this->em->getUnitOfWork();
574 7
        $persister = $uow->getCollectionPersister($association);
575 7
        $hasCache  = ($persister instanceof CachedPersister);
576 7
        $key       = null;
577
578 7
        if (! $hasCache) {
579 3
            return $this->persister->loadManyToManyCollection($association, $sourceEntity, $collection);
580
        }
581
582 4
        $ownerId = $uow->getEntityIdentifier($collection->getOwner());
583 4
        $key     = $this->buildCollectionCacheKey($association, $ownerId);
584 4
        $list    = $persister->loadCollectionCache($collection, $key);
585
586 4
        if ($list !== null) {
587 4
            if ($this->cacheLogger) {
588 4
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
589
            }
590
591 4
            return $list;
592
        }
593
594 2
        $list = $this->persister->loadManyToManyCollection($association, $sourceEntity, $collection);
595
596 2
        $persister->storeCollectionCache($key, $list);
597
598 2
        if ($this->cacheLogger) {
599 2
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
600
        }
601
602 2
        return $list;
603
    }
604
605
    /**
606
     * {@inheritdoc}
607
     */
608 17
    public function loadOneToManyCollection(
609
        OneToManyAssociationMetadata $association,
610
        $sourceEntity,
611
        PersistentCollection $collection
612
    ) {
613 17
        $uow       = $this->em->getUnitOfWork();
614 17
        $persister = $uow->getCollectionPersister($association);
615 17
        $hasCache  = ($persister instanceof CachedPersister);
616
617 17
        if (! $hasCache) {
618 3
            return $this->persister->loadOneToManyCollection($association, $sourceEntity, $collection);
619
        }
620
621 14
        $ownerId = $uow->getEntityIdentifier($collection->getOwner());
622 14
        $key     = $this->buildCollectionCacheKey($association, $ownerId);
623 14
        $list    = $persister->loadCollectionCache($collection, $key);
624
625 14
        if ($list !== null) {
626 11
            if ($this->cacheLogger) {
627 11
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
628
            }
629
630 11
            return $list;
631
        }
632
633 13
        $list = $this->persister->loadOneToManyCollection($association, $sourceEntity, $collection);
634
635 13
        $persister->storeCollectionCache($key, $list);
636
637 13
        if ($this->cacheLogger) {
638 13
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
639
        }
640
641 13
        return $list;
642
    }
643
644
    /**
645
     * {@inheritdoc}
646
     */
647 5
    public function loadToOneEntity(ToOneAssociationMetadata $association, $sourceEntity, array $identifier = [])
648
    {
649 5
        return $this->persister->loadToOneEntity($association, $sourceEntity, $identifier);
650
    }
651
652
    /**
653
     * {@inheritdoc}
654
     */
655 3
    public function lock(array $criteria, $lockMode)
656
    {
657 3
        $this->persister->lock($criteria, $lockMode);
658 3
    }
659
660
    /**
661
     * {@inheritdoc}
662
     */
663 3
    public function refresh(array $id, $entity, $lockMode = null)
664
    {
665 3
        $this->persister->refresh($id, $entity, $lockMode);
666 3
    }
667
668
    /**
669
     * @param mixed[] $ownerId
670
     *
671
     * @return CollectionCacheKey
672
     */
673 18
    protected function buildCollectionCacheKey(AssociationMetadata $association, $ownerId)
674
    {
675
        /** @var ClassMetadata $metadata */
676 18
        $metadata = $this->metadataFactory->getMetadataFor($association->getSourceEntity());
677
678 18
        return new CollectionCacheKey($metadata->getRootClassName(), $association->getName(), $ownerId);
679
    }
680
}
681