Made NTFS junction detection more reliable and added unit tests for the junction functions.

main
Niels Keurentjes 9 years ago
parent 1b1462317c
commit b71c67239d

@ -585,19 +585,23 @@ class Filesystem
/** /**
* Creates an NTFS junction. * Creates an NTFS junction.
* *
* @param string $originDir * @param string $target
* @param string $targetDir * @param string $junction
*/ */
public function junction($originDir, $targetDir) public function junction($target, $junction)
{ {
if (defined('PHP_WINDOWS_VERSION_BUILD')) { if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$cmd = sprintf('mklink /J %s %s', throw new \LogicException(sprintf('Function %s is not available on non-Windows platform', __CLASS__));
ProcessExecutor::escape(str_replace('/', DIRECTORY_SEPARATOR, $targetDir)), }
ProcessExecutor::escape(realpath($originDir))); if (!is_dir($target)) {
if ($this->getProcess()->execute($cmd) === 0) throw new IOException(sprintf('Cannot junction to "%s" as it is not a directory.', $target), 0, null, $target);
return; }
$cmd = sprintf('mklink /J %s %s',
ProcessExecutor::escape(str_replace('/', DIRECTORY_SEPARATOR, $junction)),
ProcessExecutor::escape(realpath($target)));
if ($this->getProcess()->execute($cmd, $output) !== 0) {
throw new IOException(sprintf('Failed to create junction to "%s" at "%s".', $target, $junction), 0, null, $target);
} }
throw new IOException(sprintf('Failed to create junction from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir);
} }
/** /**
@ -611,9 +615,12 @@ class Filesystem
if (!defined('PHP_WINDOWS_VERSION_BUILD')) { if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
return false; return false;
} }
$normalized = rtrim(str_replace('/', DIRECTORY_SEPARATOR, $junction), DIRECTORY_SEPARATOR); if (!is_dir($junction) || is_link($junction)) {
$real = rtrim(realpath($normalized), DIRECTORY_SEPARATOR); return false;
return is_dir($normalized) && ($normalized !== $real); }
// Junctions have no link stat but are otherwise indistinguishable from real directories
$stat = lstat($junction);
return ($stat['mode'] === 0);
} }
/** /**
@ -628,7 +635,10 @@ class Filesystem
return false; return false;
} }
$junction = rtrim(str_replace('/', DIRECTORY_SEPARATOR, $junction), DIRECTORY_SEPARATOR); $junction = rtrim(str_replace('/', DIRECTORY_SEPARATOR, $junction), DIRECTORY_SEPARATOR);
if (!$this->isJunction($junction)) {
throw new IOException(sprintf('%s is not a junction and thus cannot be removed as one', $junction));
}
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape($junction)); $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape($junction));
return $this->getProcess()->execute($cmd) === 0; return ($this->getProcess()->execute($cmd) === 0);
} }
} }

@ -266,4 +266,33 @@ class FilesystemTest extends TestCase
$this->assertFalse(file_exists($symlinkedTrailingSlash)); $this->assertFalse(file_exists($symlinkedTrailingSlash));
$this->assertFalse(file_exists($symlinked)); $this->assertFalse(file_exists($symlinked));
} }
public function testJunctions()
{
@mkdir($this->workingDir . '/real/nesting/testing', 0777, true);
$fs = new Filesystem();
// Non-Windows systems do not support this and will return false on all tests, and an exception on creation
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->assertFalse($fs->isJunction($this->workingDir));
$this->assertFalse($fs->removeJunction($this->workingDir));
$this->setExpectedException('LogicException', 'not available on non-Windows platform');
}
$target = $this->workingDir . '/real/../real/nesting';
$junction = $this->workingDir . '/junction';
// Create and detect junction
$fs->junction($target, $junction);
$this->assertTrue($fs->isJunction($junction));
$this->assertFalse($fs->isJunction($target));
$this->assertTrue($fs->isJunction($target . '/../../junction'));
$this->assertFalse($fs->isJunction($junction . '/../real'));
$this->assertTrue($fs->isJunction($junction . '/../junction'));
// Remove junction
$this->assertTrue(is_dir($junction));
$this->assertTrue($fs->removeJunction($junction));
$this->assertFalse(is_dir($junction));
}
} }

Loading…
Cancel
Save