feature #40144 [Filesystem] Remove dirs atomically if possible (nicolas-grekas)

This PR was merged into the 5.3-dev branch.

Discussion
----------

[Filesystem] Remove dirs atomically if possible

| Q             | A
| ------------- | ---
| Branch?       | 5.x
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #27578
| License       | MIT
| Doc PR        | no need to

Commits
-------

17bccca9c6 [Filesystem] remove dirs atomically if possible
This commit is contained in:
Alexander M. Turek 2021-02-11 20:02:08 +01:00
commit d97ab18aa6

View File

@ -162,6 +162,12 @@ class Filesystem
} elseif (!\is_array($files)) { } elseif (!\is_array($files)) {
$files = [$files]; $files = [$files];
} }
self::doRemove($files, false);
}
private static function doRemove(array $files, bool $isRecursive): void
{
$files = array_reverse($files); $files = array_reverse($files);
foreach ($files as $file) { foreach ($files as $file) {
if (is_link($file)) { if (is_link($file)) {
@ -170,10 +176,35 @@ class Filesystem
throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError); throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError);
} }
} elseif (is_dir($file)) { } elseif (is_dir($file)) {
$this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); if (!$isRecursive) {
$tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.'));
if (!self::box('rmdir', $file) && file_exists($file)) { if (file_exists($tmpName)) {
throw new IOException(sprintf('Failed to remove directory "%s": ', $file).self::$lastError); try {
self::doRemove([$tmpName], true);
} catch (IOException $e) {
}
}
if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) {
$origFile = $file;
$file = $tmpName;
} else {
$origFile = null;
}
}
$files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS);
self::doRemove(iterator_to_array($files, true), true);
if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) {
$lastError = self::$lastError;
if (null !== $origFile && self::box('rename', $file, $origFile)) {
$file = $origFile;
}
throw new IOException(sprintf('Failed to remove directory "%s": ', $file).$lastError);
} }
} elseif (!self::box('unlink', $file) && (false !== strpos(self::$lastError, 'Permission denied') || file_exists($file))) { } elseif (!self::box('unlink', $file) && (false !== strpos(self::$lastError, 'Permission denied') || file_exists($file))) {
throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError); throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError);