bug #16987 [FileSystem] Windows fix (flip111)
This PR was merged into the 2.3 branch.
Discussion
----------
[FileSystem] Windows fix
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | https://github.com/symfony/symfony/issues/16783
| License | MIT
| Doc PR |
* Fixes edge case on windows where PHP does not generate a PHP Warning but instead returns a wrong result https://bugs.php.net/bug.php?id=71103
* Improved error reporting on `unlink` used in `remove()`
Commits
-------
0d5f7e2
Update FileSystem
This commit is contained in:
commit
7ce861d09a
@ -100,6 +100,10 @@ class Filesystem
|
|||||||
public function exists($files)
|
public function exists($files)
|
||||||
{
|
{
|
||||||
foreach ($this->toIterator($files) as $file) {
|
foreach ($this->toIterator($files) as $file) {
|
||||||
|
if ('\\' === DIRECTORY_SEPARATOR AND strlen($file) > 258) {
|
||||||
|
throw new IOException(sprintf('Could not check if file exist because path length exceeds 258 characters for file "%s"', $file));
|
||||||
|
}
|
||||||
|
|
||||||
if (!file_exists($file)) {
|
if (!file_exists($file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -139,7 +143,7 @@ class Filesystem
|
|||||||
$files = iterator_to_array($this->toIterator($files));
|
$files = iterator_to_array($this->toIterator($files));
|
||||||
$files = array_reverse($files);
|
$files = array_reverse($files);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if (!file_exists($file) && !is_link($file)) {
|
if (!$this->exists($file) && !is_link($file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +161,8 @@ class Filesystem
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (true !== @unlink($file)) {
|
if (true !== @unlink($file)) {
|
||||||
throw new IOException(sprintf('Failed to remove file %s', $file));
|
$error = error_get_last();
|
||||||
|
throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +258,7 @@ class Filesystem
|
|||||||
public function rename($origin, $target, $overwrite = false)
|
public function rename($origin, $target, $overwrite = false)
|
||||||
{
|
{
|
||||||
// we check that target does not exist
|
// we check that target does not exist
|
||||||
if (!$overwrite && is_readable($target)) {
|
if (!$overwrite && $this->isReadable($target)) {
|
||||||
throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
|
throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +267,22 @@ class Filesystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether a file exists and is readable.
|
||||||
|
*
|
||||||
|
* @param string $filename Path to the file.
|
||||||
|
*
|
||||||
|
* @throws IOException When windows path is longer than 258 characters
|
||||||
|
*/
|
||||||
|
private function isReadable($filename)
|
||||||
|
{
|
||||||
|
if ('\\' === DIRECTORY_SEPARATOR AND strlen($filename) > 258) {
|
||||||
|
throw new IOException(sprintf('Could not check if file is readable because path length exceeds 258 characters for file "%s"', $filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_readable($filename);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a symbolic link or copy a directory.
|
* Creates a symbolic link or copy a directory.
|
||||||
*
|
*
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Filesystem\Filesystem;
|
|||||||
class FilesystemTest extends \PHPUnit_Framework_TestCase
|
class FilesystemTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
private $umask;
|
private $umask;
|
||||||
|
private $longPathNamesWindows = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
@ -56,6 +57,12 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
|
if (!empty($this->longPathNamesWindows)) {
|
||||||
|
foreach ($this->longPathNamesWindows as $path) {
|
||||||
|
exec('DEL '.$path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->filesystem->remove($this->workspace);
|
$this->filesystem->remove($this->workspace);
|
||||||
umask($this->umask);
|
umask($this->umask);
|
||||||
}
|
}
|
||||||
@ -354,6 +361,28 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertTrue($this->filesystem->exists($basePath.'folder'));
|
$this->assertTrue($this->filesystem->exists($basePath.'folder'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Filesystem\Exception\IOException
|
||||||
|
*/
|
||||||
|
public function testFilesExistsFails()
|
||||||
|
{
|
||||||
|
if ('\\' !== DIRECTORY_SEPARATOR) {
|
||||||
|
$this->markTestSkipped('Test covers edge case on Windows only.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$basePath = $this->workspace.'\\directory\\';
|
||||||
|
|
||||||
|
$oldPath = getcwd();
|
||||||
|
mkdir($basePath);
|
||||||
|
chdir($basePath);
|
||||||
|
$file = str_repeat('T', 259 - strlen($basePath));
|
||||||
|
$path = $basePath.$file;
|
||||||
|
exec('TYPE NUL >>'.$file); // equivalent of touch, we can not use the php touch() here because it suffers from the same limitation
|
||||||
|
$this->longPathNamesWindows[] = $path; // save this so we can clean up later
|
||||||
|
chdir($oldPath);
|
||||||
|
$this->filesystem->exists($path);
|
||||||
|
}
|
||||||
|
|
||||||
public function testFilesExistsTraversableObjectOfFilesAndDirectories()
|
public function testFilesExistsTraversableObjectOfFilesAndDirectories()
|
||||||
{
|
{
|
||||||
$basePath = $this->workspace.DIRECTORY_SEPARATOR;
|
$basePath = $this->workspace.DIRECTORY_SEPARATOR;
|
||||||
|
Reference in New Issue
Block a user