summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Cache/Persister
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
committerpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
commitbf6655a534a6775d30cafa67bd801276bda1d98d (patch)
treec6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/orm/src/Cache/Persister
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/orm/src/Cache/Persister')
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/CachedPersister.php25
-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
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Entity/AbstractEntityPersister.php557
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Entity/CachedEntityPersister.php20
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php85
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php19
-rw-r--r--vendor/doctrine/orm/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php105
11 files changed, 1216 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Cache/Persister/CachedPersister.php b/vendor/doctrine/orm/src/Cache/Persister/CachedPersister.php
new file mode 100644
index 0000000..223692c
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/CachedPersister.php
@@ -0,0 +1,25 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister;
6
7use Doctrine\ORM\Cache\Region;
8
9/**
10 * Interface for persister that support second level cache.
11 */
12interface CachedPersister
13{
14 /**
15 * Perform whatever processing is encapsulated here after completion of the transaction.
16 */
17 public function afterTransactionComplete(): void;
18
19 /**
20 * Perform whatever processing is encapsulated here after completion of the rolled-back.
21 */
22 public function afterTransactionRolledBack(): void;
23
24 public function getCacheRegion(): Region;
25}
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}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Entity/AbstractEntityPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Entity/AbstractEntityPersister.php
new file mode 100644
index 0000000..9f371d8
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Entity/AbstractEntityPersister.php
@@ -0,0 +1,557 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Entity;
6
7use Doctrine\Common\Collections\Criteria;
8use Doctrine\Common\Collections\Order;
9use Doctrine\DBAL\LockMode;
10use Doctrine\ORM\Cache;
11use Doctrine\ORM\Cache\CollectionCacheKey;
12use Doctrine\ORM\Cache\EntityCacheKey;
13use Doctrine\ORM\Cache\EntityHydrator;
14use Doctrine\ORM\Cache\Logging\CacheLogger;
15use Doctrine\ORM\Cache\Persister\CachedPersister;
16use Doctrine\ORM\Cache\QueryCacheKey;
17use Doctrine\ORM\Cache\Region;
18use Doctrine\ORM\Cache\TimestampCacheKey;
19use Doctrine\ORM\Cache\TimestampRegion;
20use Doctrine\ORM\EntityManagerInterface;
21use Doctrine\ORM\Mapping\AssociationMapping;
22use Doctrine\ORM\Mapping\ClassMetadata;
23use Doctrine\ORM\Mapping\ClassMetadataFactory;
24use Doctrine\ORM\PersistentCollection;
25use Doctrine\ORM\Persisters\Entity\EntityPersister;
26use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
27use Doctrine\ORM\Query\ResultSetMapping;
28use Doctrine\ORM\UnitOfWork;
29
30use function array_merge;
31use function assert;
32use function serialize;
33use function sha1;
34
35abstract class AbstractEntityPersister implements CachedEntityPersister
36{
37 protected UnitOfWork $uow;
38 protected ClassMetadataFactory $metadataFactory;
39
40 /** @var mixed[] */
41 protected array $queuedCache = [];
42
43 protected TimestampRegion $timestampRegion;
44 protected TimestampCacheKey $timestampKey;
45 protected EntityHydrator $hydrator;
46 protected Cache $cache;
47 protected CacheLogger|null $cacheLogger = null;
48 protected string $regionName;
49
50 /**
51 * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
52 *
53 * @var array<string>|null
54 */
55 protected array|null $joinedAssociations = null;
56
57 public function __construct(
58 protected EntityPersister $persister,
59 protected Region $region,
60 EntityManagerInterface $em,
61 protected ClassMetadata $class,
62 ) {
63 $configuration = $em->getConfiguration();
64 $cacheConfig = $configuration->getSecondLevelCacheConfiguration();
65 $cacheFactory = $cacheConfig->getCacheFactory();
66
67 $this->cache = $em->getCache();
68 $this->regionName = $region->getName();
69 $this->uow = $em->getUnitOfWork();
70 $this->metadataFactory = $em->getMetadataFactory();
71 $this->cacheLogger = $cacheConfig->getCacheLogger();
72 $this->timestampRegion = $cacheFactory->getTimestampRegion();
73 $this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
74 $this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
75 }
76
77 public function addInsert(object $entity): void
78 {
79 $this->persister->addInsert($entity);
80 }
81
82 /**
83 * {@inheritDoc}
84 */
85 public function getInserts(): array
86 {
87 return $this->persister->getInserts();
88 }
89
90 public function getSelectSQL(
91 array|Criteria $criteria,
92 AssociationMapping|null $assoc = null,
93 LockMode|int|null $lockMode = null,
94 int|null $limit = null,
95 int|null $offset = null,
96 array|null $orderBy = null,
97 ): string {
98 return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
99 }
100
101 public function getCountSQL(array|Criteria $criteria = []): string
102 {
103 return $this->persister->getCountSQL($criteria);
104 }
105
106 public function getInsertSQL(): string
107 {
108 return $this->persister->getInsertSQL();
109 }
110
111 public function getResultSetMapping(): ResultSetMapping
112 {
113 return $this->persister->getResultSetMapping();
114 }
115
116 public function getSelectConditionStatementSQL(
117 string $field,
118 mixed $value,
119 AssociationMapping|null $assoc = null,
120 string|null $comparison = null,
121 ): string {
122 return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);
123 }
124
125 public function exists(object $entity, Criteria|null $extraConditions = null): bool
126 {
127 if ($extraConditions === null) {
128 $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity));
129
130 if ($this->region->contains($key)) {
131 return true;
132 }
133 }
134
135 return $this->persister->exists($entity, $extraConditions);
136 }
137
138 public function getCacheRegion(): Region
139 {
140 return $this->region;
141 }
142
143 public function getEntityHydrator(): EntityHydrator
144 {
145 return $this->hydrator;
146 }
147
148 public function storeEntityCache(object $entity, EntityCacheKey $key): bool
149 {
150 $class = $this->class;
151 $className = DefaultProxyClassNameResolver::getClass($entity);
152
153 if ($className !== $this->class->name) {
154 $class = $this->metadataFactory->getMetadataFor($className);
155 }
156
157 $entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
158 $cached = $this->region->put($key, $entry);
159
160 if ($cached) {
161 $this->cacheLogger?->entityCachePut($this->regionName, $key);
162 }
163
164 return $cached;
165 }
166
167 private function storeJoinedAssociations(object $entity): void
168 {
169 if ($this->joinedAssociations === null) {
170 $associations = [];
171
172 foreach ($this->class->associationMappings as $name => $assoc) {
173 if (
174 isset($assoc->cache) &&
175 ($assoc->isToOne()) &&
176 ($assoc->fetch === ClassMetadata::FETCH_EAGER || ! $assoc->isOwningSide())
177 ) {
178 $associations[] = $name;
179 }
180 }
181
182 $this->joinedAssociations = $associations;
183 }
184
185 foreach ($this->joinedAssociations as $name) {
186 $assoc = $this->class->associationMappings[$name];
187 $assocEntity = $this->class->getFieldValue($entity, $name);
188
189 if ($assocEntity === null) {
190 continue;
191 }
192
193 $assocId = $this->uow->getEntityIdentifier($assocEntity);
194 $assocMetadata = $this->metadataFactory->getMetadataFor($assoc->targetEntity);
195 $assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocId);
196 $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity);
197
198 $assocPersister->storeEntityCache($assocEntity, $assocKey);
199 }
200 }
201
202 /**
203 * Generates a string of currently query
204 *
205 * @param string[]|Criteria $criteria
206 * @param array<string, Order>|null $orderBy
207 */
208 protected function getHash(
209 string $query,
210 array|Criteria $criteria,
211 array|null $orderBy = null,
212 int|null $limit = null,
213 int|null $offset = null,
214 ): string {
215 [$params] = $criteria instanceof Criteria
216 ? $this->persister->expandCriteriaParameters($criteria)
217 : $this->persister->expandParameters($criteria);
218
219 return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
220 }
221
222 /**
223 * {@inheritDoc}
224 */
225 public function expandParameters(array $criteria): array
226 {
227 return $this->persister->expandParameters($criteria);
228 }
229
230 /**
231 * {@inheritDoc}
232 */
233 public function expandCriteriaParameters(Criteria $criteria): array
234 {
235 return $this->persister->expandCriteriaParameters($criteria);
236 }
237
238 public function getClassMetadata(): ClassMetadata
239 {
240 return $this->persister->getClassMetadata();
241 }
242
243 /**
244 * {@inheritDoc}
245 */
246 public function getManyToManyCollection(
247 AssociationMapping $assoc,
248 object $sourceEntity,
249 int|null $offset = null,
250 int|null $limit = null,
251 ): array {
252 return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit);
253 }
254
255 /**
256 * {@inheritDoc}
257 */
258 public function getOneToManyCollection(
259 AssociationMapping $assoc,
260 object $sourceEntity,
261 int|null $offset = null,
262 int|null $limit = null,
263 ): array {
264 return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);
265 }
266
267 public function getOwningTable(string $fieldName): string
268 {
269 return $this->persister->getOwningTable($fieldName);
270 }
271
272 public function executeInserts(): void
273 {
274 // The commit order/foreign key relationships may make it necessary that multiple calls to executeInsert()
275 // are performed, so collect all the new entities.
276 $newInserts = $this->persister->getInserts();
277
278 if ($newInserts) {
279 $this->queuedCache['insert'] = array_merge($this->queuedCache['insert'] ?? [], $newInserts);
280 }
281
282 $this->persister->executeInserts();
283 }
284
285 /**
286 * {@inheritDoc}
287 */
288 public function load(
289 array $criteria,
290 object|null $entity = null,
291 AssociationMapping|null $assoc = null,
292 array $hints = [],
293 LockMode|int|null $lockMode = null,
294 int|null $limit = null,
295 array|null $orderBy = null,
296 ): object|null {
297 if ($entity !== null || $assoc !== null || $hints !== [] || $lockMode !== null) {
298 return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
299 }
300
301 //handle only EntityRepository#findOneBy
302 $query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
303 $hash = $this->getHash($query, $criteria);
304 $rsm = $this->getResultSetMapping();
305 $queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
306 $queryCache = $this->cache->getQueryCache($this->regionName);
307 $result = $queryCache->get($queryKey, $rsm);
308
309 if ($result !== null) {
310 $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);
311
312 return $result[0];
313 }
314
315 $result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
316
317 if ($result === null) {
318 return null;
319 }
320
321 $cached = $queryCache->put($queryKey, $rsm, [$result]);
322
323 $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);
324
325 if ($cached) {
326 $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);
327 }
328
329 return $result;
330 }
331
332 /**
333 * {@inheritDoc}
334 */
335 public function loadAll(
336 array $criteria = [],
337 array|null $orderBy = null,
338 int|null $limit = null,
339 int|null $offset = null,
340 ): array {
341 $query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
342 $hash = $this->getHash($query, $criteria);
343 $rsm = $this->getResultSetMapping();
344 $queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
345 $queryCache = $this->cache->getQueryCache($this->regionName);
346 $result = $queryCache->get($queryKey, $rsm);
347
348 if ($result !== null) {
349 $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);
350
351 return $result;
352 }
353
354 $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
355 $cached = $queryCache->put($queryKey, $rsm, $result);
356
357 if ($result) {
358 $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);
359 }
360
361 if ($cached) {
362 $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);
363 }
364
365 return $result;
366 }
367
368 /**
369 * {@inheritDoc}
370 */
371 public function loadById(array $identifier, object|null $entity = null): object|null
372 {
373 $cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier);
374 $cacheEntry = $this->region->get($cacheKey);
375 $class = $this->class;
376
377 if ($cacheEntry !== null) {
378 if ($cacheEntry->class !== $this->class->name) {
379 $class = $this->metadataFactory->getMetadataFor($cacheEntry->class);
380 }
381
382 $cachedEntity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity);
383
384 if ($cachedEntity !== null) {
385 $this->cacheLogger?->entityCacheHit($this->regionName, $cacheKey);
386
387 return $cachedEntity;
388 }
389 }
390
391 $entity = $this->persister->loadById($identifier, $entity);
392
393 if ($entity === null) {
394 return null;
395 }
396
397 $class = $this->class;
398 $className = DefaultProxyClassNameResolver::getClass($entity);
399
400 if ($className !== $this->class->name) {
401 $class = $this->metadataFactory->getMetadataFor($className);
402 }
403
404 $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);
405 $cached = $this->region->put($cacheKey, $cacheEntry);
406
407 if ($cached && ($this->joinedAssociations === null || $this->joinedAssociations)) {
408 $this->storeJoinedAssociations($entity);
409 }
410
411 if ($cached) {
412 $this->cacheLogger?->entityCachePut($this->regionName, $cacheKey);
413 }
414
415 $this->cacheLogger?->entityCacheMiss($this->regionName, $cacheKey);
416
417 return $entity;
418 }
419
420 public function count(array|Criteria $criteria = []): int
421 {
422 return $this->persister->count($criteria);
423 }
424
425 /**
426 * {@inheritDoc}
427 */
428 public function loadCriteria(Criteria $criteria): array
429 {
430 $orderBy = $criteria->orderings();
431 $limit = $criteria->getMaxResults();
432 $offset = $criteria->getFirstResult();
433 $query = $this->persister->getSelectSQL($criteria);
434 $hash = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
435 $rsm = $this->getResultSetMapping();
436 $queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
437 $queryCache = $this->cache->getQueryCache($this->regionName);
438 $cacheResult = $queryCache->get($queryKey, $rsm);
439
440 if ($cacheResult !== null) {
441 $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey);
442
443 return $cacheResult;
444 }
445
446 $result = $this->persister->loadCriteria($criteria);
447 $cached = $queryCache->put($queryKey, $rsm, $result);
448
449 if ($result) {
450 $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey);
451 }
452
453 if ($cached) {
454 $this->cacheLogger?->queryCachePut($this->regionName, $queryKey);
455 }
456
457 return $result;
458 }
459
460 /**
461 * {@inheritDoc}
462 */
463 public function loadManyToManyCollection(
464 AssociationMapping $assoc,
465 object $sourceEntity,
466 PersistentCollection $collection,
467 ): array {
468 $persister = $this->uow->getCollectionPersister($assoc);
469 $hasCache = ($persister instanceof CachedPersister);
470
471 if (! $hasCache) {
472 return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
473 }
474
475 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
476 $key = $this->buildCollectionCacheKey($assoc, $ownerId);
477 $list = $persister->loadCollectionCache($collection, $key);
478
479 if ($list !== null) {
480 $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
481
482 return $list;
483 }
484
485 $list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
486
487 $persister->storeCollectionCache($key, $list);
488
489 $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
490
491 return $list;
492 }
493
494 public function loadOneToManyCollection(
495 AssociationMapping $assoc,
496 object $sourceEntity,
497 PersistentCollection $collection,
498 ): mixed {
499 $persister = $this->uow->getCollectionPersister($assoc);
500 $hasCache = ($persister instanceof CachedPersister);
501
502 if (! $hasCache) {
503 return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
504 }
505
506 $ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
507 $key = $this->buildCollectionCacheKey($assoc, $ownerId);
508 $list = $persister->loadCollectionCache($collection, $key);
509
510 if ($list !== null) {
511 $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
512
513 return $list;
514 }
515
516 $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
517
518 $persister->storeCollectionCache($key, $list);
519
520 $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
521
522 return $list;
523 }
524
525 /**
526 * {@inheritDoc}
527 */
528 public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null
529 {
530 return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier);
531 }
532
533 /**
534 * {@inheritDoc}
535 */
536 public function lock(array $criteria, LockMode|int $lockMode): void
537 {
538 $this->persister->lock($criteria, $lockMode);
539 }
540
541 /**
542 * {@inheritDoc}
543 */
544 public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void
545 {
546 $this->persister->refresh($id, $entity, $lockMode);
547 }
548
549 /** @param array<string, mixed> $ownerId */
550 protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId): CollectionCacheKey
551 {
552 $metadata = $this->metadataFactory->getMetadataFor($association->sourceEntity);
553 assert($metadata instanceof ClassMetadata);
554
555 return new CollectionCacheKey($metadata->rootEntityName, $association->fieldName, $ownerId);
556 }
557}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Entity/CachedEntityPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Entity/CachedEntityPersister.php
new file mode 100644
index 0000000..5fba56f
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Entity/CachedEntityPersister.php
@@ -0,0 +1,20 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Entity;
6
7use Doctrine\ORM\Cache\EntityCacheKey;
8use Doctrine\ORM\Cache\EntityHydrator;
9use Doctrine\ORM\Cache\Persister\CachedPersister;
10use Doctrine\ORM\Persisters\Entity\EntityPersister;
11
12/**
13 * Interface for second level cache entity persisters.
14 */
15interface CachedEntityPersister extends CachedPersister, EntityPersister
16{
17 public function getEntityHydrator(): EntityHydrator;
18
19 public function storeEntityCache(object $entity, EntityCacheKey $key): bool;
20}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php
new file mode 100644
index 0000000..43c76ab
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php
@@ -0,0 +1,85 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Entity;
6
7use Doctrine\ORM\Cache\EntityCacheKey;
8
9/**
10 * Specific non-strict read/write cached entity persister
11 */
12class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
13{
14 public function afterTransactionComplete(): void
15 {
16 $isChanged = false;
17
18 if (isset($this->queuedCache['insert'])) {
19 foreach ($this->queuedCache['insert'] as $entity) {
20 $isChanged = $this->updateCache($entity, $isChanged);
21 }
22 }
23
24 if (isset($this->queuedCache['update'])) {
25 foreach ($this->queuedCache['update'] as $entity) {
26 $isChanged = $this->updateCache($entity, $isChanged);
27 }
28 }
29
30 if (isset($this->queuedCache['delete'])) {
31 foreach ($this->queuedCache['delete'] as $key) {
32 $this->region->evict($key);
33
34 $isChanged = true;
35 }
36 }
37
38 if ($isChanged) {
39 $this->timestampRegion->update($this->timestampKey);
40 }
41
42 $this->queuedCache = [];
43 }
44
45 public function afterTransactionRolledBack(): void
46 {
47 $this->queuedCache = [];
48 }
49
50 public function delete(object $entity): bool
51 {
52 $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
53 $deleted = $this->persister->delete($entity);
54
55 if ($deleted) {
56 $this->region->evict($key);
57 }
58
59 $this->queuedCache['delete'][] = $key;
60
61 return $deleted;
62 }
63
64 public function update(object $entity): void
65 {
66 $this->persister->update($entity);
67
68 $this->queuedCache['update'][] = $entity;
69 }
70
71 private function updateCache(object $entity, bool $isChanged): bool
72 {
73 $class = $this->metadataFactory->getMetadataFor($entity::class);
74 $key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
75 $entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
76 $cached = $this->region->put($key, $entry);
77 $isChanged = $isChanged || $cached;
78
79 if ($cached) {
80 $this->cacheLogger?->entityCachePut($this->regionName, $key);
81 }
82
83 return $isChanged;
84 }
85}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php
new file mode 100644
index 0000000..4cd1784
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php
@@ -0,0 +1,19 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Entity;
6
7use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyEntity;
8use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
9
10/**
11 * Specific read-only region entity persister
12 */
13class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister
14{
15 public function update(object $entity): void
16 {
17 throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity));
18 }
19}
diff --git a/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php
new file mode 100644
index 0000000..a1ea0dc
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php
@@ -0,0 +1,105 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Persister\Entity;
6
7use Doctrine\ORM\Cache\ConcurrentRegion;
8use Doctrine\ORM\Cache\EntityCacheKey;
9use Doctrine\ORM\EntityManagerInterface;
10use Doctrine\ORM\Mapping\ClassMetadata;
11use Doctrine\ORM\Persisters\Entity\EntityPersister;
12
13/**
14 * Specific read-write entity persister
15 */
16class ReadWriteCachedEntityPersister extends AbstractEntityPersister
17{
18 public function __construct(EntityPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, ClassMetadata $class)
19 {
20 parent::__construct($persister, $region, $em, $class);
21 }
22
23 public function afterTransactionComplete(): void
24 {
25 $isChanged = true;
26
27 if (isset($this->queuedCache['update'])) {
28 foreach ($this->queuedCache['update'] as $item) {
29 $this->region->evict($item['key']);
30
31 $isChanged = true;
32 }
33 }
34
35 if (isset($this->queuedCache['delete'])) {
36 foreach ($this->queuedCache['delete'] as $item) {
37 $this->region->evict($item['key']);
38
39 $isChanged = true;
40 }
41 }
42
43 if ($isChanged) {
44 $this->timestampRegion->update($this->timestampKey);
45 }
46
47 $this->queuedCache = [];
48 }
49
50 public function afterTransactionRolledBack(): void
51 {
52 if (isset($this->queuedCache['update'])) {
53 foreach ($this->queuedCache['update'] as $item) {
54 $this->region->evict($item['key']);
55 }
56 }
57
58 if (isset($this->queuedCache['delete'])) {
59 foreach ($this->queuedCache['delete'] as $item) {
60 $this->region->evict($item['key']);
61 }
62 }
63
64 $this->queuedCache = [];
65 }
66
67 public function delete(object $entity): bool
68 {
69 $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
70 $lock = $this->region->lock($key);
71 $deleted = $this->persister->delete($entity);
72
73 if ($deleted) {
74 $this->region->evict($key);
75 }
76
77 if ($lock === null) {
78 return $deleted;
79 }
80
81 $this->queuedCache['delete'][] = [
82 'lock' => $lock,
83 'key' => $key,
84 ];
85
86 return $deleted;
87 }
88
89 public function update(object $entity): void
90 {
91 $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
92 $lock = $this->region->lock($key);
93
94 $this->persister->update($entity);
95
96 if ($lock === null) {
97 return;
98 }
99
100 $this->queuedCache['update'][] = [
101 'lock' => $lock,
102 'key' => $key,
103 ];
104 }
105}