PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Queue / FileSeekableQueue.php
wp-staging / Framework / Queue Last commit date
Storage 1 day ago FileSeekableQueue.php 1 day ago FinishedQueueException.php 5 years ago Queue.php 1 day ago QueueInterface.php 1 day ago SeekableQueueInterface.php 1 day ago
FileSeekableQueue.php
310 lines
1 <?php
2
3 namespace WPStaging\Framework\Queue;
4
5 use RuntimeException;
6 use WPStaging\Core\Utils\Logger;
7 use WPStaging\Framework\Adapter\Directory;
8 use WPStaging\Framework\Filesystem\FileObject;
9 use WPStaging\Framework\Filesystem\Filesystem;
10 use WPStaging\Framework\Utils\Cache\Cache;
11
12 use function WPStaging\functions\debug_log;
13
14 class FileSeekableQueue implements SeekableQueueInterface, \SeekableIterator
15 {
16
17 const FILE_EXTENSION = 'cache.php';
18
19
20 protected $taskName;
21
22
23 protected $handle;
24
25
26 protected $fileGenerator;
27
28
29 protected $directory;
30
31
32 protected $filesystem;
33
34
35 protected $offsetBefore;
36
37
38 protected $needsUnlock = false;
39
40
41 protected $isWriteOnly;
42
43 public function __construct(Directory $directory, Filesystem $filesystem)
44 {
45 $this->directory = $directory;
46 $this->filesystem = $filesystem;
47 }
48
49 public function __destruct()
50 {
51 $this->shutdown();
52 }
53
54
55
56
57
58
59 public function setup($taskName, $queueMode = SeekableQueueInterface::MODE_READ_WRITE)
60 {
61 $this->taskName = $taskName;
62
63 $extension = self::FILE_EXTENSION;
64 $path = "{$this->directory->getCacheDirectory()}$taskName.$extension";
65
66 $this->filesystem->mkdir(dirname($path), true);
67
68 $isNewQueue = $this->createQueue($path);
69
70
71 if ($queueMode !== SeekableQueueInterface::MODE_WRITE && $queueMode !== SeekableQueueInterface::MODE_READ_WRITE) {
72 throw new \BadMethodCallException();
73 }
74
75 $this->handle = new FileObject($path, $queueMode);
76 $this->handle->setFlags(FileObject::DROP_NEW_LINE);
77 $this->fileGenerator = $this->initializeGenerator();
78
79 $this->isWriteOnly = $queueMode === SeekableQueueInterface::MODE_WRITE;
80
81 if ($this->isWriteOnly) {
82 $waitedTimes = 0;
83 do {
84 $wouldBlock = false;
85
86
87
88
89
90 $locked = $this->handle->flock(LOCK_EX | LOCK_NB, $wouldBlock) || (bool)!$wouldBlock;
91
92 if (!$locked) {
93 usleep(250000);
94 $waitedTimes++;
95 if ($waitedTimes > 5) {
96 throw new \RuntimeException(sprintf(esc_html__('Could not acquire exclusive lock for writing to Queue file: %s.task', 'wp-staging'), $this->taskName));
97 }
98 }
99 } while (!$locked);
100
101 $this->needsUnlock = true;
102 }
103
104
105 if ($isNewQueue) {
106 $this->enqueue(Cache::PHP_HEADER);
107 }
108 }
109
110
111
112
113 protected function initializeGenerator()
114 {
115 while ($this->handle->valid()) {
116 $this->offsetBefore = $this->handle->ftell();
117 yield $this->handle->readAndMoveNext();
118 }
119 }
120
121 #[\ReturnTypeWillChange]
122 public function current()
123 {
124 return $this->fileGenerator->current();
125 }
126
127 #[\ReturnTypeWillChange]
128 public function next()
129 {
130 $this->fileGenerator->next();
131 }
132
133 #[\ReturnTypeWillChange]
134 public function key()
135 {
136 return $this->fileGenerator->key();
137 }
138
139 #[\ReturnTypeWillChange]
140 public function valid()
141 {
142 return $this->fileGenerator->valid();
143 }
144
145 #[\ReturnTypeWillChange]
146 public function rewind()
147 {
148 $this->handle->fseek(0);
149 }
150
151 #[\ReturnTypeWillChange]
152 public function seek($offset)
153 {
154 $this->handle->fseek($offset);
155 }
156
157
158
159
160 public function isFinished(): bool
161 {
162 return $this->handle->eof();
163 }
164
165
166
167
168
169 public function retry($dequeue = true)
170 {
171 $this->seek($this->offsetBefore);
172
173 if ($dequeue) {
174 return $this->dequeue();
175 }
176 }
177
178
179
180
181
182 public function enqueue($data)
183 {
184 $trimmedData = trim($data);
185
186 if ($this->isWriteOnly) {
187 $this->handle->fwrite($trimmedData . PHP_EOL);
188
189 return $this->handle->ftell();
190 }
191
192 $currentOffset = $this->handle->ftell();
193
194 $this->handle->fseek(0, SEEK_END);
195 $this->handle->flock(LOCK_EX);
196 $this->handle->fwrite($trimmedData . PHP_EOL);
197 $this->handle->flock(LOCK_UN);
198
199 $offsetEndOfQueue = $this->handle->ftell();
200 $this->handle->fseek($currentOffset);
201
202 return $offsetEndOfQueue;
203 }
204
205
206
207
208 public function dequeue()
209 {
210 if ($this->isWriteOnly) {
211 throw new \BadMethodCallException('Trying to read from read-only Queue');
212 }
213
214 $first = is_null($this->offsetBefore);
215
216 if (!$first) {
217 $this->next();
218 }
219
220 $current = $this->current();
221 if ($current !== rtrim(Cache::PHP_HEADER)) {
222 return $current;
223 }
224
225 $this->next();
226 return $this->current();
227 }
228
229
230
231
232
233 public function enqueueMany(array $data = [])
234 {
235 foreach ($data as $item) {
236 if (is_scalar($item)) {
237 $this->enqueue((string)$item);
238 }
239 }
240
241 return $this->handle->ftell();
242 }
243
244
245
246
247 public function reset()
248 {
249 $this->handle->ftruncate(0);
250 }
251
252
253
254
255 public function getOffset()
256 {
257 if (!isset($this->handle) || !$this->handle instanceof FileObject) {
258 return false;
259 }
260
261 return $this->handle->ftell();
262 }
263
264
265
266
267 public function shutdown()
268 {
269 if ($this->needsUnlock && $this->handle instanceof FileObject) {
270 $this->unlockObject();
271 }
272
273 $this->handle = null;
274 }
275
276
277
278
279 protected function unlockObject()
280 {
281 try {
282 $this->handle->flock(LOCK_UN);
283 } catch (\Throwable $e) {
284 $message = $e->getMessage();
285 if ($message !== 'Object not initialized') {
286 debug_log("Unable to unlock handle " . $this->taskName . '.task : ' . $message, Logger::TYPE_DEBUG);
287 }
288 }
289 }
290
291
292
293
294
295
296 protected function createQueue(string $path): bool
297 {
298 if (file_exists($path)) {
299 return false;
300 }
301
302 if (!touch($path)) {
303 debug_log("Check if there is enough free space and the file permissions are 644 or 755. Could not create file: $path");
304 throw new RuntimeException(sprintf(esc_html__("Check if there is enough free space and the file permissions are 644 or 755. Could not create file: %s", 'wp-staging'), $path));
305 }
306
307 return true;
308 }
309 }
310