Skip to content

Commit

Permalink
Update FileSystem
Browse files Browse the repository at this point in the history
* 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()`
  • Loading branch information
flip111 committed Dec 12, 2015
1 parent e6c3575 commit 7279b38
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
27 changes: 24 additions & 3 deletions Filesystem.php
Expand Up @@ -100,6 +100,10 @@ public function mkdir($dirs, $mode = 0777)
public function exists($files)
{
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)) {
return false;
}
Expand Down Expand Up @@ -139,7 +143,7 @@ public function remove($files)
$files = iterator_to_array($this->toIterator($files));
$files = array_reverse($files);
foreach ($files as $file) {
if (!file_exists($file) && !is_link($file)) {
if (!$this->exists($file) && !is_link($file)) {
continue;
}

Expand All @@ -157,7 +161,8 @@ public function remove($files)
}
} else {
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']));
}
}
}
Expand Down Expand Up @@ -253,7 +258,7 @@ public function chgrp($files, $group, $recursive = false)
public function rename($origin, $target, $overwrite = false)
{
// 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));
}

Expand All @@ -262,6 +267,22 @@ public function rename($origin, $target, $overwrite = false)
}
}

/**
* 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.
*
Expand Down
29 changes: 29 additions & 0 deletions Tests/FilesystemTest.php
Expand Up @@ -19,6 +19,7 @@
class FilesystemTest extends \PHPUnit_Framework_TestCase
{
private $umask;
private $longPathNamesWindows = array();

/**
* @var string
Expand Down Expand Up @@ -56,6 +57,12 @@ protected function setUp()

protected function tearDown()
{
if (!empty($this->longPathNamesWindows)) {
foreach ($this->longPathNamesWindows as $path) {
exec('DEL '.$path);
}
}

$this->filesystem->remove($this->workspace);
umask($this->umask);
}
Expand Down Expand Up @@ -354,6 +361,28 @@ public function testFilesExists()
$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()
{
$basePath = $this->workspace.DIRECTORY_SEPARATOR;
Expand Down

0 comments on commit 7279b38

Please sign in to comment.