PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.0
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 / Filesystem / AbstractFileObject.php
wp-staging / Framework / Filesystem Last commit date
Filters 6 months ago Scanning 5 years ago AbstractFileObject.php 1 year ago AbstractFilesystemScanner.php 2 months ago DebugLogReader.php 2 years ago DirectoryListing.php 5 months ago DiskWriteCheck.php 5 months ago FileObject.php 1 year ago Filesystem.php 6 months ago FilesystemExceptions.php 5 years ago FilesystemScanner.php 1 week ago FilesystemScannerDto.php 1 week ago FilterableDirectoryIterator.php 1 year ago LegacyFileRulesTrait.php 1 week ago LogCleanup.php 5 months ago LogFiles.php 1 year ago MissingFileException.php 3 years ago OPcache.php 5 months ago PartIdentifier.php 8 months ago PathChecker.php 2 years ago PathIdentifier.php 6 months ago Permissions.php 5 months ago WpUploadsFolderSymlinker.php 1 week ago
AbstractFileObject.php
313 lines
1 <?php
2
3 namespace WPStaging\Framework\Filesystem;
4
5 /**
6 * This is an abstract class of WP Staging implementation of SplFileObject class
7 * Which doesn't depend upon core plugin, so it can be used with wpstg-restorer standalone tool
8 * Don't import core PHP namespaces as it is not required for standalone tool as it will be bundled into single file
9 * When building in standalone tool, this class will be renamed to FileObject and changed to final class
10 * Also this class should not use any wp core functions or classes.
11 */
12 abstract class AbstractFileObject extends \SplFileObject
13 {
14 const MODE_READ = 'rb'; // read only, binary
15 const MODE_WRITE = 'wb'; // write only, binary
16 const MODE_APPEND = 'ab'; // append with create, binary
17 const MODE_APPEND_AND_READ = 'ab+'; // append with read and create if not exists, binary
18 const MODE_WRITE_SAFE = 'xb'; // write if exists E_WARNING & return false, binary
19 const MODE_WRITE_UNSAFE = 'cb'; // append, if exists cursor to top, binary
20
21 /** @var int */
22 protected $totalLines = null;
23
24 /** @var bool */
25 protected $fgetsUsedOnKey0 = false;
26
27 /** @var bool */
28 protected $fseekUsed = false;
29
30 public function __construct(string $fullPath, string $openMode = self::MODE_READ)
31 {
32 try {
33 parent::__construct($fullPath, $openMode);
34 } catch (\Throwable $e) {
35 throw $e;
36 }
37 }
38
39 /** @return int */
40 public function totalLines(bool $useParent = false): int
41 {
42 if ($this->totalLines !== null) {
43 return $this->totalLines;
44 }
45
46 if ($useParent) {
47 $currentKey = $this->keyUseParent();
48 $this->seekUseParent(PHP_INT_MAX);
49 $this->totalLines = $this->keyUseParent();
50
51 if ($currentKey < 0) {
52 $currentKey = 0;
53 }
54
55 $this->seekUseParent($currentKey);
56 } else {
57 $currentKey = $this->key();
58 if ($currentKey < 0) {
59 $currentKey = 0;
60 }
61
62 $this->seek(PHP_INT_MAX);
63 $this->totalLines = $this->key();
64 $this->seek($currentKey);
65 }
66
67 if ($this->totalLines > 0) {
68 if (PHP_VERSION === '8.2.0RC3' || version_compare(PHP_VERSION, '8.2.0', '>=')) {
69 $this->totalLines += 1;
70 }
71
72 if (version_compare(PHP_VERSION, '8.1', '>') && version_compare(PHP_VERSION, '8.1.11', '<=')) {
73 $this->totalLines += 1;
74 }
75 }
76
77 return $this->totalLines;
78 }
79
80 /**
81 * Override SplFileObject::seek()
82 * Alternative function for SplFileObject::seek() that behaves identical in all PHP Versions.
83 *
84 * There was a major change in PHP 8.0.1 where after using `SplFileObject::seek($line)`, the first subsequent
85 * call to `SplFileObject::fgets()` does not increase the line pointer anymore as it did in earlier version since PHP 5.x
86 * @see https://bugs.php.net/bug.php?id=81551
87 *
88 * Note: This will remove READ_AHEAD flag while execution to deliver reliable and identical results as READ_AHEAD tells
89 * SplFileObject to read on next() and rewind() too which our custom seek() makes use of.
90 * This would disturb this seek() implementation and would lead to fatal errors if 'cpu load' setting is 'medium' or 'high'
91 *
92 *
93 * @param int $offset The zero-based line number to seek to.
94 * @return void
95 * @throws Exception
96 */
97 #[\ReturnTypeWillChange]
98 public function seek($offset)
99 {
100 if ($offset < 0) {
101 throw new \Exception("Can't seek file: " . $this->getPathname() . " to negative offset: $offset");
102 }
103
104 $this->fseekUsed = false;
105 $this->fgetsUsedOnKey0 = false;
106 if ($offset === 0 || version_compare(PHP_VERSION, '8.0.1', '<')) {
107 parent::seek($offset);
108 return;
109 }
110
111 $offset -= 1;
112
113 if ($this->totalLines !== null && $offset >= $this->totalLines) {
114 $offset += 1;
115 }
116
117 $originalFlags = $this->getFlags();
118 $newFlags = $originalFlags & ~self::READ_AHEAD;
119 $this->setFlags($newFlags);
120
121 parent::seek($offset);
122
123 if ($this->eof()) {
124 $this->current();
125 $this->totalLines = $this->key();
126 return;
127 }
128
129 $this->current();
130 $this->next();
131 $this->current();
132
133 $this->setFlags($originalFlags);
134 }
135
136 /**
137 * SplFileObject::fgets() is not consistent after SplFileObject::fseek() between php 5.x/7.x and php 8.0.1.
138 * We could either make fgets consistent after SplFileObject::seek() or SplFileObject::fseek()
139 * This implementation makes it consistent after SplFileObject::seek across all PHP versions up to 8.0.1.
140 * Use readAndMoveNext() instead if you want to achieve consistent behavior of SplFileObject::fgets after SplFileObject::fseek.
141 *
142 * @deprecated 4.2.13 Use readAndMoveNext instead as it is hard to make fgets against multiple php version after seek(0)
143 *
144 * @return string
145 */
146 public function fgets(): string
147 {
148 if ($this->key() === 0 || version_compare(PHP_VERSION, '8.0.1', '<')) {
149 $this->fgetsUsedOnKey0 = true;
150 return parent::fgets();
151 }
152
153 $originalFlags = $this->getFlags();
154 $newFlags = $originalFlags & ~self::READ_AHEAD;
155 $this->setFlags($newFlags);
156
157 $line = $this->current();
158 $this->next();
159
160 if (version_compare(PHP_VERSION, '8.0.19', '<')) {
161 $line = $this->current();
162 }
163
164 if (version_compare(PHP_VERSION, '8.1', '>') && version_compare(PHP_VERSION, '8.1.6', '<')) {
165 $line = $this->current();
166 }
167
168 if (!$this->fseekUsed) {
169 $line = $this->current();
170 }
171
172 $this->setFlags($originalFlags);
173 return $line;
174 }
175
176 /** @return int */
177 #[\ReturnTypeWillChange]
178 public function key(): int
179 {
180 if (!$this->fgetsUsedOnKey0 || version_compare(PHP_VERSION, '8.0.19', '<')) {
181 return parent::key();
182 }
183
184 if (version_compare(PHP_VERSION, '8.1', '>') && version_compare(PHP_VERSION, '8.1.6', '<')) {
185 return parent::key();
186 }
187
188 return parent::key() - 1;
189 }
190
191 /**
192 * Seek to a position
193 *
194 * @param int $offset The value to start from added to the $whence
195 * @param int $whence values are:
196 * SEEK_SET - Set position equal to offset bytes.
197 * SEEK_CUR - Set position to current location plus offset.
198 * SEEK_END - Set position to end-of-file plus offset.
199 * @return int
200 */
201 #[\ReturnTypeWillChange]
202 public function fseek($offset, $whence = SEEK_SET): int
203 {
204 if (version_compare(PHP_VERSION, '8.0.19', '<')) {
205 return parent::fseek($offset, $whence);
206 }
207
208 if (version_compare(PHP_VERSION, '8.1', '>') && version_compare(PHP_VERSION, '8.1.6', '<')) {
209 return parent::fseek($offset, $whence);
210 }
211
212 // After calling parent::fseek() and $this->fgets() two or three times it starts to act different on PHP >= 8.0.19, PHP >= 8.1.6 and PHP >= 8.2.
213 // Calling it three times helps to write a consistent fseek() for the above mentioned PHP versions.
214 for ($i = 0; $i < 3; $i++) {
215 parent::fseek(0);
216 $this->fgets();
217 }
218
219 $this->fseekUsed = true;
220 return parent::fseek((int)$offset, $whence);
221 }
222
223 /**
224 * SplFileObject::fgets() is not consistent after SplFileObject::fseek() between php 5.x/7.x and php 8.0.1.
225 * Use this method instead if you want to achieve consistent behavior of SplFileObject::fgets after SplFileObject::fseek across all PHP versions up to PHP 8.0.1.
226 * READ_AHEAD flag will not have any affect on this method. It's disabled.
227 *
228 * @var bool $useFgets default false. Setting this to true will use fgets on PHP < 8.0.1
229 *
230 * @return string
231 */
232 public function readAndMoveNext(bool $useFgets = false): string
233 {
234 if ($useFgets && version_compare(PHP_VERSION, '8.0.1', '<')) {
235 return parent::fgets();
236 }
237
238 $originalFlags = $this->getFlags();
239 $newFlags = $originalFlags & ~self::READ_AHEAD;
240 $this->setFlags($newFlags);
241
242 $line = $this->current();
243 $this->next();
244
245 $this->setFlags($originalFlags);
246 return $line;
247 }
248
249 /** @return bool */
250 public function isSqlFile(): bool
251 {
252 return $this->getExtension() === 'sql';
253 }
254
255 public function fgetsUseParent(): string
256 {
257 return parent::fgets();
258 }
259
260 /** @return int */
261 public function keyUseParent(): int
262 {
263 return parent::key();
264 }
265
266 /** @return void */
267 public function seekUseParent(int $offset)
268 {
269 parent::seek($offset);
270 }
271
272 /**
273 * Manage file locking.
274 * On WinOS a file is always locked when using SplFileObject. So, do nothing here and just return true on WinOS.
275 * @param int $operation
276 * @param int|null $wouldBlock
277 * @return bool
278 *
279 * Note: Adding type for $operation throw error in IDE
280 */
281 #[\ReturnTypeWillChange]
282 public function flock($operation, &$wouldBlock = null): bool
283 {
284 if ($this->isWindowsOs()) {
285 return true;
286 }
287
288 $parentMethodFlock = 'parent::flock';
289 if (version_compare(PHP_VERSION, '8.2', '>=')) {
290 // phpcs:ignore SlevomatCodingStandard.PHP.ForbiddenClasses.ForbiddenClass
291 $parentMethodFlock = \SplFileObject::class . '::flock';
292 }
293
294 if (!is_callable($parentMethodFlock)) {
295 return false;
296 }
297
298 return parent::flock($operation, $wouldBlock);
299 }
300
301 /**
302 * @return bool
303 */
304 protected function isWindowsOs(): bool
305 {
306 if (function_exists('wpstgIsWindowsOs')) {
307 return wpstgIsWindowsOs();
308 }
309
310 return false;
311 }
312 }
313