summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Cache/Region
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/orm/src/Cache/Region')
-rw-r--r--vendor/doctrine/orm/src/Cache/Region/DefaultRegion.php113
-rw-r--r--vendor/doctrine/orm/src/Cache/Region/FileLockRegion.php194
-rw-r--r--vendor/doctrine/orm/src/Cache/Region/UpdateTimestampCache.php20
3 files changed, 327 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Cache/Region/DefaultRegion.php b/vendor/doctrine/orm/src/Cache/Region/DefaultRegion.php
new file mode 100644
index 0000000..0576195
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Region/DefaultRegion.php
@@ -0,0 +1,113 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Region;
6
7use Doctrine\ORM\Cache\CacheEntry;
8use Doctrine\ORM\Cache\CacheKey;
9use Doctrine\ORM\Cache\CollectionCacheEntry;
10use Doctrine\ORM\Cache\Lock;
11use Doctrine\ORM\Cache\Region;
12use Psr\Cache\CacheItemInterface;
13use Psr\Cache\CacheItemPoolInterface;
14use Traversable;
15
16use function array_map;
17use function iterator_to_array;
18use function strtr;
19
20/**
21 * The simplest cache region compatible with all doctrine-cache drivers.
22 */
23class DefaultRegion implements Region
24{
25 private const REGION_KEY_SEPARATOR = '_';
26 private const REGION_PREFIX = 'DC2_REGION_';
27
28 public function __construct(
29 private readonly string $name,
30 private readonly CacheItemPoolInterface $cacheItemPool,
31 private readonly int $lifetime = 0,
32 ) {
33 }
34
35 public function getName(): string
36 {
37 return $this->name;
38 }
39
40 public function contains(CacheKey $key): bool
41 {
42 return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key));
43 }
44
45 public function get(CacheKey $key): CacheEntry|null
46 {
47 $item = $this->cacheItemPool->getItem($this->getCacheEntryKey($key));
48 $entry = $item->isHit() ? $item->get() : null;
49
50 if (! $entry instanceof CacheEntry) {
51 return null;
52 }
53
54 return $entry;
55 }
56
57 public function getMultiple(CollectionCacheEntry $collection): array|null
58 {
59 $keys = array_map(
60 $this->getCacheEntryKey(...),
61 $collection->identifiers,
62 );
63 /** @var iterable<string, CacheItemInterface> $items */
64 $items = $this->cacheItemPool->getItems($keys);
65 if ($items instanceof Traversable) {
66 $items = iterator_to_array($items);
67 }
68
69 $result = [];
70 foreach ($keys as $arrayKey => $cacheKey) {
71 if (! isset($items[$cacheKey]) || ! $items[$cacheKey]->isHit()) {
72 return null;
73 }
74
75 $entry = $items[$cacheKey]->get();
76 if (! $entry instanceof CacheEntry) {
77 return null;
78 }
79
80 $result[$arrayKey] = $entry;
81 }
82
83 return $result;
84 }
85
86 public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
87 {
88 $item = $this->cacheItemPool
89 ->getItem($this->getCacheEntryKey($key))
90 ->set($entry);
91
92 if ($this->lifetime > 0) {
93 $item->expiresAfter($this->lifetime);
94 }
95
96 return $this->cacheItemPool->save($item);
97 }
98
99 public function evict(CacheKey $key): bool
100 {
101 return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key));
102 }
103
104 public function evictAll(): bool
105 {
106 return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name);
107 }
108
109 private function getCacheEntryKey(CacheKey $key): string
110 {
111 return self::REGION_PREFIX . $this->name . self::REGION_KEY_SEPARATOR . strtr($key->hash, '{}()/\@:', '________');
112 }
113}
diff --git a/vendor/doctrine/orm/src/Cache/Region/FileLockRegion.php b/vendor/doctrine/orm/src/Cache/Region/FileLockRegion.php
new file mode 100644
index 0000000..bedd6a6
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Region/FileLockRegion.php
@@ -0,0 +1,194 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Region;
6
7use Doctrine\ORM\Cache\CacheEntry;
8use Doctrine\ORM\Cache\CacheKey;
9use Doctrine\ORM\Cache\CollectionCacheEntry;
10use Doctrine\ORM\Cache\ConcurrentRegion;
11use Doctrine\ORM\Cache\Lock;
12use Doctrine\ORM\Cache\Region;
13use InvalidArgumentException;
14
15use function array_filter;
16use function array_map;
17use function chmod;
18use function file_get_contents;
19use function file_put_contents;
20use function fileatime;
21use function glob;
22use function is_dir;
23use function is_file;
24use function is_writable;
25use function mkdir;
26use function sprintf;
27use function time;
28use function unlink;
29
30use const DIRECTORY_SEPARATOR;
31use const LOCK_EX;
32
33/**
34 * Very naive concurrent region, based on file locks.
35 */
36class FileLockRegion implements ConcurrentRegion
37{
38 final public const LOCK_EXTENSION = 'lock';
39
40 /**
41 * @param numeric-string|int $lockLifetime
42 *
43 * @throws InvalidArgumentException
44 */
45 public function __construct(
46 private readonly Region $region,
47 private readonly string $directory,
48 private readonly string|int $lockLifetime,
49 ) {
50 if (! is_dir($directory) && ! @mkdir($directory, 0775, true)) {
51 throw new InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $directory));
52 }
53
54 if (! is_writable($directory)) {
55 throw new InvalidArgumentException(sprintf('The directory "%s" is not writable.', $directory));
56 }
57 }
58
59 private function isLocked(CacheKey $key, Lock|null $lock = null): bool
60 {
61 $filename = $this->getLockFileName($key);
62
63 if (! is_file($filename)) {
64 return false;
65 }
66
67 $time = $this->getLockTime($filename);
68 $content = $this->getLockContent($filename);
69
70 if ($content === false || $time === false) {
71 @unlink($filename);
72
73 return false;
74 }
75
76 if ($lock && $content === $lock->value) {
77 return false;
78 }
79
80 // outdated lock
81 if ($time + $this->lockLifetime <= time()) {
82 @unlink($filename);
83
84 return false;
85 }
86
87 return true;
88 }
89
90 private function getLockFileName(CacheKey $key): string
91 {
92 return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION;
93 }
94
95 private function getLockContent(string $filename): string|false
96 {
97 return @file_get_contents($filename);
98 }
99
100 private function getLockTime(string $filename): int|false
101 {
102 return @fileatime($filename);
103 }
104
105 public function getName(): string
106 {
107 return $this->region->getName();
108 }
109
110 public function contains(CacheKey $key): bool
111 {
112 if ($this->isLocked($key)) {
113 return false;
114 }
115
116 return $this->region->contains($key);
117 }
118
119 public function get(CacheKey $key): CacheEntry|null
120 {
121 if ($this->isLocked($key)) {
122 return null;
123 }
124
125 return $this->region->get($key);
126 }
127
128 public function getMultiple(CollectionCacheEntry $collection): array|null
129 {
130 if (array_filter(array_map($this->isLocked(...), $collection->identifiers))) {
131 return null;
132 }
133
134 return $this->region->getMultiple($collection);
135 }
136
137 public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
138 {
139 if ($this->isLocked($key, $lock)) {
140 return false;
141 }
142
143 return $this->region->put($key, $entry);
144 }
145
146 public function evict(CacheKey $key): bool
147 {
148 if ($this->isLocked($key)) {
149 @unlink($this->getLockFileName($key));
150 }
151
152 return $this->region->evict($key);
153 }
154
155 public function evictAll(): bool
156 {
157 // The check below is necessary because on some platforms glob returns false
158 // when nothing matched (even though no errors occurred)
159 $filenames = glob(sprintf('%s/*.%s', $this->directory, self::LOCK_EXTENSION)) ?: [];
160
161 foreach ($filenames as $filename) {
162 @unlink($filename);
163 }
164
165 return $this->region->evictAll();
166 }
167
168 public function lock(CacheKey $key): Lock|null
169 {
170 if ($this->isLocked($key)) {
171 return null;
172 }
173
174 $lock = Lock::createLockRead();
175 $filename = $this->getLockFileName($key);
176
177 if (@file_put_contents($filename, $lock->value, LOCK_EX) === false) {
178 return null;
179 }
180
181 chmod($filename, 0664);
182
183 return $lock;
184 }
185
186 public function unlock(CacheKey $key, Lock $lock): bool
187 {
188 if ($this->isLocked($key, $lock)) {
189 return false;
190 }
191
192 return @unlink($this->getLockFileName($key));
193 }
194}
diff --git a/vendor/doctrine/orm/src/Cache/Region/UpdateTimestampCache.php b/vendor/doctrine/orm/src/Cache/Region/UpdateTimestampCache.php
new file mode 100644
index 0000000..aa75a90
--- /dev/null
+++ b/vendor/doctrine/orm/src/Cache/Region/UpdateTimestampCache.php
@@ -0,0 +1,20 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Cache\Region;
6
7use Doctrine\ORM\Cache\CacheKey;
8use Doctrine\ORM\Cache\TimestampCacheEntry;
9use Doctrine\ORM\Cache\TimestampRegion;
10
11/**
12 * Tracks the timestamps of the most recent updates to particular keys.
13 */
14class UpdateTimestampCache extends DefaultRegion implements TimestampRegion
15{
16 public function update(CacheKey $key): void
17 {
18 $this->put($key, new TimestampCacheEntry());
19 }
20}