summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Cache/Persister/Collection
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/orm/src/Cache/Persister/Collection')
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Collection/AbstractCollectionPersister.php168
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Collection/CachedCollectionPersister.php36
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php74
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php24
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php103
5 files changed, 405 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Collection/AbstractCollectionPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Collection/AbstractCollectionPersister.php
new file mode 100644
index 0000000..8c087a8
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Collection/AbstractCollectionPersister.php
@@ -0,0 +1,168 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Collection;
6
7use Doctrine\Common\Collections\Collection;
8use Doctrine\Common\Collections\Criteria;
9use Doctrine\ORM\Cache\CollectionCacheKey;
10use Doctrine\ORM\Cache\CollectionHydrator;
11use Doctrine\ORM\Cache\Logging\CacheLogger;
12use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
13use Doctrine\ORM\Cache\Region;
14use Doctrine\ORM\EntityManagerInterface;
15use Doctrine\ORM\Mapping\AssociationMapping;
16use Doctrine\ORM\Mapping\ClassMetadata;
17use Doctrine\ORM\Mapping\ClassMetadataFactory;
18use Doctrine\ORM\PersistentCollection;
19use Doctrine\ORM\Persisters\Collection\CollectionPersister;
20use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
21use Doctrine\ORM\UnitOfWork;
22
23use function array_values;
24use function assert;
25use function count;
26
27abstract class AbstractCollectionPersister implements CachedCollectionPersister
28{
29 protected UnitOfWork $uow;
30 protected ClassMetadataFactory $metadataFactory;
31 protected ClassMetadata $sourceEntity;
32 protected ClassMetadata $targetEntity;
33
34 /** @var mixed[] */
35 protected array $queuedCache = [];
36
37 protected string $regionName;
38 protected CollectionHydrator $hydrator;
39 protected CacheLogger|null $cacheLogger;
40
41 public function __construct(
42 protected CollectionPersister $persister,
43 protected Region $region,
44 EntityManagerInterface $em,
45 protected AssociationMapping $association,
46 ) {
47 $configuration = $em->getConfiguration();
48 $cacheConfig = $configuration->getSecondLevelCacheConfiguration();
49 $cacheFactory = $cacheConfig->getCacheFactory();
50
51 $this->regionName = $region->getName();
52 $this->uow = $em->getUnitOfWork();
53 $this->metadataFactory = $em->getMetadataFactory();
54 $this->cacheLogger = $cacheConfig->getCacheLogger();
55 $this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association);
56 $this->sourceEntity = $em->getClassMetadata($association->sourceEntity);
57 $this->targetEntity = $em->getClassMetadata($association->targetEntity);
58 }
59
60 public function getCacheRegion(): Region
61 {
62 return $this->region;
63 }
64
65 public function getSourceEntityMetadata(): ClassMetadata
66 {
67 return $this->sourceEntity;
68 }
69
70 public function getTargetEntityMetadata(): ClassMetadata
71 {
72 return $this->targetEntity;
73 }
74
75 public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null
76 {
77 $cache = $this->region->get($key);
78
79 if ($cache === null) {
80 return null;
81 }
82
83 return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
84 }
85
86 public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void
87 {
88 $associationMapping = $this->sourceEntity->associationMappings[$key->association];
89 $targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);
90 assert($targetPersister instanceof CachedEntityPersister);
91 $targetRegion = $targetPersister->getCacheRegion();
92 $targetHydrator = $targetPersister->getEntityHydrator();
93
94 // Only preserve ordering if association configured it
95 if (! $associationMapping->isIndexed()) {
96 // Elements may be an array or a Collection
97 $elements = array_values($elements instanceof Collection ? $elements->getValues() : $elements);
98 }
99
100 $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);
101
102 foreach ($entry->identifiers as $index => $entityKey) {
103 if ($targetRegion->contains($entityKey)) {
104 continue;
105 }
106
107 $class = $this->targetEntity;
108 $className = DefaultProxyClassNameResolver::getClass($elements[$index]);
109
110 if ($className !== $this->targetEntity->name) {
111 $class = $this->metadataFactory->getMetadataFor($className);
112 }
113
114 $entity = $elements[$index];
115 $entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
116
117 $targetRegion->put($entityKey, $entityEntry);
118 }
119
120 if ($this->region->put($key, $entry)) {
121 $this->cacheLogger?->collectionCachePut($this->regionName, $key);
122 }
123 }
124
125 public function contains(PersistentCollection $collection, object $element): bool
126 {
127 return $this->persister->contains($collection, $element);
128 }
129
130 public function containsKey(PersistentCollection $collection, mixed $key): bool
131 {
132 return $this->persister->containsKey($collection, $key);
133 }
134
135 public function count(PersistentCollection $collection): int
136 {
137 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
138 $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
139 $entry = $this->region->get($key);
140
141 if ($entry !== null) {
142 return count($entry->identifiers);
143 }
144
145 return $this->persister->count($collection);
146 }
147
148 public function get(PersistentCollection $collection, mixed $index): mixed
149 {
150 return $this->persister->get($collection, $index);
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array
157 {
158 return $this->persister->slice($collection, $offset, $length);
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array
165 {
166 return $this->persister->loadCriteria($collection, $criteria);
167 }
168}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Collection/CachedCollectionPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Collection/CachedCollectionPersister.php
new file mode 100644
index 0000000..6b10c80
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Collection/CachedCollectionPersister.php
@@ -0,0 +1,36 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Collection;
6
7use Doctrine\Common\Collections\Collection;
8use Doctrine\ORM\Cache\CollectionCacheKey;
9use Doctrine\ORM\Cache\Persister\CachedPersister;
10use Doctrine\ORM\Mapping\ClassMetadata;
11use Doctrine\ORM\PersistentCollection;
12use Doctrine\ORM\Persisters\Collection\CollectionPersister;
13
14/**
15 * Interface for second level cache collection persisters.
16 */
17interface CachedCollectionPersister extends CachedPersister, CollectionPersister
18{
19 public function getSourceEntityMetadata(): ClassMetadata;
20
21 public function getTargetEntityMetadata(): ClassMetadata;
22
23 /**
24 * Loads a collection from cache
25 *
26 * @return mixed[]|null
27 */
28 public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null;
29
30 /**
31 * Stores a collection into cache
32 *
33 * @param mixed[]|Collection $elements
34 */
35 public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void;
36}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php
new file mode 100644
index 0000000..ac861f4
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php
@@ -0,0 +1,74 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Collection;
6
7use Doctrine\ORM\Cache\CollectionCacheKey;
8use Doctrine\ORM\PersistentCollection;
9
10use function spl_object_id;
11
12class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister
13{
14 public function afterTransactionComplete(): void
15 {
16 if (isset($this->queuedCache['update'])) {
17 foreach ($this->queuedCache['update'] as $item) {
18 $this->storeCollectionCache($item['key'], $item['list']);
19 }
20 }
21
22 if (isset($this->queuedCache['delete'])) {
23 foreach ($this->queuedCache['delete'] as $key) {
24 $this->region->evict($key);
25 }
26 }
27
28 $this->queuedCache = [];
29 }
30
31 public function afterTransactionRolledBack(): void
32 {
33 $this->queuedCache = [];
34 }
35
36 public function delete(PersistentCollection $collection): void
37 {
38 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
39 $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
40
41 $this->persister->delete($collection);
42
43 $this->queuedCache['delete'][spl_object_id($collection)] = $key;
44 }
45
46 public function update(PersistentCollection $collection): void
47 {
48 $isInitialized = $collection->isInitialized();
49 $isDirty = $collection->isDirty();
50
51 if (! $isInitialized && ! $isDirty) {
52 return;
53 }
54
55 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
56 $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
57
58 // Invalidate non initialized collections OR ordered collection
59 if ($isDirty && ! $isInitialized || $this->association->isOrdered()) {
60 $this->persister->update($collection);
61
62 $this->queuedCache['delete'][spl_object_id($collection)] = $key;
63
64 return;
65 }
66
67 $this->persister->update($collection);
68
69 $this->queuedCache['update'][spl_object_id($collection)] = [
70 'key' => $key,
71 'list' => $collection,
72 ];
73 }
74}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php
new file mode 100644
index 0000000..96e0a4b
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php
@@ -0,0 +1,24 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Collection;
6
7use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyCollection;
8use Doctrine\ORM\PersistentCollection;
9use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
10
11class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister
12{
13 public function update(PersistentCollection $collection): void
14 {
15 if ($collection->isDirty() && $collection->getSnapshot()) {
16 throw CannotUpdateReadOnlyCollection::fromEntityAndField(
17 DefaultProxyClassNameResolver::getClass($collection->getOwner()),
18 $this->association->fieldName,
19 );
20 }
21
22 parent::update($collection);
23 }
24}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php
new file mode 100644
index 0000000..347a065
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php
@@ -0,0 +1,103 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Collection;
6
7use Doctrine\ORM\Cache\CollectionCacheKey;
8use Doctrine\ORM\Cache\ConcurrentRegion;
9use Doctrine\ORM\EntityManagerInterface;
10use Doctrine\ORM\Mapping\AssociationMapping;
11use Doctrine\ORM\PersistentCollection;
12use Doctrine\ORM\Persisters\Collection\CollectionPersister;
13
14use function spl_object_id;
15
16class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
17{
18 public function __construct(
19 CollectionPersister $persister,
20 ConcurrentRegion $region,
21 EntityManagerInterface $em,
22 AssociationMapping $association,
23 ) {
24 parent::__construct($persister, $region, $em, $association);
25 }
26
27 public function afterTransactionComplete(): void
28 {
29 if (isset($this->queuedCache['update'])) {
30 foreach ($this->queuedCache['update'] as $item) {
31 $this->region->evict($item['key']);
32 }
33 }
34
35 if (isset($this->queuedCache['delete'])) {
36 foreach ($this->queuedCache['delete'] as $item) {
37 $this->region->evict($item['key']);
38 }
39 }
40
41 $this->queuedCache = [];
42 }
43
44 public function afterTransactionRolledBack(): void
45 {
46 if (isset($this->queuedCache['update'])) {
47 foreach ($this->queuedCache['update'] as $item) {
48 $this->region->evict($item['key']);
49 }
50 }
51
52 if (isset($this->queuedCache['delete'])) {
53 foreach ($this->queuedCache['delete'] as $item) {
54 $this->region->evict($item['key']);
55 }
56 }
57
58 $this->queuedCache = [];
59 }
60
61 public function delete(PersistentCollection $collection): void
62 {
63 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
64 $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
65 $lock = $this->region->lock($key);
66
67 $this->persister->delete($collection);
68
69 if ($lock === null) {
70 return;
71 }
72
73 $this->queuedCache['delete'][spl_object_id($collection)] = [
74 'key' => $key,
75 'lock' => $lock,
76 ];
77 }
78
79 public function update(PersistentCollection $collection): void
80 {
81 $isInitialized = $collection->isInitialized();
82 $isDirty = $collection->isDirty();
83
84 if (! $isInitialized && ! $isDirty) {
85 return;
86 }
87
88 $this->persister->update($collection);
89
90 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
91 $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
92 $lock = $this->region->lock($key);
93
94 if ($lock === null) {
95 return;
96 }
97
98 $this->queuedCache['update'][spl_object_id($collection)] = [
99 'key' => $key,
100 'lock' => $lock,
101 ];
102 }
103}