diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php
index 7387a94cc..f79642066 100644
--- a/src/Composer/Cache.php
+++ b/src/Composer/Cache.php
@@ -136,7 +136,7 @@ class Cache
{
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if ($this->enabled && file_exists($this->root . $file)) {
- return unlink($this->root . $file);
+ return $this->filesystem->unlink($this->root . $file);
}
return false;
@@ -150,7 +150,7 @@ class Cache
$finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s'));
foreach ($finder as $file) {
- unlink($file->getPathname());
+ $this->filesystem->unlink($file->getPathname());
}
$totalSize = $this->filesystem->size($this->root);
@@ -159,7 +159,7 @@ class Cache
while ($totalSize > $maxSize && $iterator->valid()) {
$filepath = $iterator->current()->getPathname();
$totalSize -= $this->filesystem->size($filepath);
- unlink($filepath);
+ $this->filesystem->unlink($filepath);
$iterator->next();
}
}
diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php
index db1fc674c..7c0a761c6 100644
--- a/src/Composer/Downloader/ArchiveDownloader.php
+++ b/src/Composer/Downloader/ArchiveDownloader.php
@@ -48,7 +48,7 @@ abstract class ArchiveDownloader extends FileDownloader
throw $e;
}
- unlink($fileName);
+ $this->filesystem->unlink($fileName);
$contentDir = $this->getFolderContent($temporaryDir);
diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php
index 7d1c4f9a7..793ae6be0 100644
--- a/src/Composer/Downloader/FileDownloader.php
+++ b/src/Composer/Downloader/FileDownloader.php
@@ -205,10 +205,7 @@ class FileDownloader implements DownloaderInterface
{
$this->io->write(" - Removing " . $package->getName() . " (" . VersionParser::formatVersion($package) . ")");
if (!$this->filesystem->removeDirectory($path)) {
- // retry after a bit on windows since it tends to be touchy with mass removals
- if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(250000) && !$this->filesystem->removeDirectory($path))) {
- throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
- }
+ throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
}
}
diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php
index ea7df270a..e653794ca 100644
--- a/src/Composer/Downloader/VcsDownloader.php
+++ b/src/Composer/Downloader/VcsDownloader.php
@@ -162,10 +162,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
$this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")");
$this->cleanChanges($package, $path, false);
if (!$this->filesystem->removeDirectory($path)) {
- // retry after a bit on windows since it tends to be touchy with mass removals
- if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(250) && !$this->filesystem->removeDirectory($path))) {
- throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
- }
+ throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
}
}
diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php
index b847eac69..4facfd494 100644
--- a/src/Composer/Installer/LibraryInstaller.php
+++ b/src/Composer/Installer/LibraryInstaller.php
@@ -259,10 +259,10 @@ class LibraryInstaller implements InstallerInterface
foreach ($binaries as $bin) {
$link = $this->binDir.'/'.basename($bin);
if (is_link($link) || file_exists($link)) {
- unlink($link);
+ $this->filesystem->unlink($link);
}
if (file_exists($link.'.bat')) {
- unlink($link.'.bat');
+ $this->filesystem->unlink($link.'.bat');
}
}
}
diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php
index b44f61f14..defadd9cf 100644
--- a/src/Composer/Installer/PearInstaller.php
+++ b/src/Composer/Installer/PearInstaller.php
@@ -77,7 +77,7 @@ class PearInstaller extends LibraryInstaller
if ($this->io->isVerbose()) {
$this->io->write(' Cleaning up');
}
- unlink($packageArchive);
+ $this->filesystem->unlink($packageArchive);
}
protected function getBinaries(PackageInterface $package)
diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php
index e9736c007..d167de463 100644
--- a/src/Composer/Util/Filesystem.php
+++ b/src/Composer/Util/Filesystem.php
@@ -36,7 +36,7 @@ class Filesystem
}
if (file_exists($file)) {
- return unlink($file);
+ return $this->unlink($file);
}
return false;
@@ -62,7 +62,7 @@ class Filesystem
public function emptyDirectory($dir, $ensureDirectoryExists = true)
{
if (file_exists($dir) && is_link($dir)) {
- unlink($dir);
+ $this->unlink($dir);
}
if ($ensureDirectoryExists) {
@@ -94,10 +94,10 @@ class Filesystem
public function removeDirectory($directory)
{
if (file_exists($directory) && is_link($directory)) {
- return unlink($directory);
+ return $this->unlink($directory);
}
- if (!is_dir($directory)) {
+ if (!file_exists($directory) || !is_dir($directory)) {
return true;
}
@@ -117,11 +117,11 @@ class Filesystem
$result = $this->getProcess()->execute($cmd, $output) === 0;
- if ($result) {
- // clear stat cache because external processes aren't tracked by the php stat cache
- clearstatcache();
+ // clear stat cache because external processes aren't tracked by the php stat cache
+ clearstatcache();
- return !is_dir($directory);
+ if ($result && !file_exists($directory)) {
+ return true;
}
return $this->removeDirectoryPhp($directory);
@@ -144,13 +144,13 @@ class Filesystem
foreach ($ri as $file) {
if ($file->isDir()) {
- rmdir($file->getPathname());
+ $this->rmdir($file->getPathname());
} else {
- unlink($file->getPathname());
+ $this->unlink($file->getPathname());
}
}
- return rmdir($directory);
+ return $this->rmdir($directory);
}
public function ensureDirectoryExists($directory)
@@ -169,6 +169,54 @@ class Filesystem
}
}
+ /**
+ * Attempts to unlink a file and in case of failure retries after 350ms on windows
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function unlink($path)
+ {
+ if (!@unlink($path)) {
+ // retry after a bit on windows since it tends to be touchy with mass removals
+ if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@unlink($path))) {
+ $error = error_get_last();
+ $message = 'Could not delete '.$path.': ' . @$error['message'];
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
+ $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
+ }
+
+ throw new \RuntimeException($message);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempts to rmdir a file and in case of failure retries after 350ms on windows
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function rmdir($path)
+ {
+ if (!@rmdir($path)) {
+ // retry after a bit on windows since it tends to be touchy with mass removals
+ if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@rmdir($path))) {
+ $error = error_get_last();
+ $message = 'Could not delete '.$path.': ' . @$error['message'];
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
+ $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
+ }
+
+ throw new \RuntimeException($message);
+ }
+ }
+
+ return true;
+ }
+
/**
* Copy then delete is a non-atomic version of {@link rename}.
*