在 Windows 7 + Apache 环境中检测 PHP 中损坏的符号链接

Detect broken symlink in PHP on Windows 7 + Apache environment

我有一个 Windows 7 开发环境,我正在使用带有 php 的 symlink() 函数的符号链接(不是联结)。一切正常,直到符号链接的目标被删除。当发生这种情况时,所有 PHP 的文件函数(file_exists()、is_file()、is_dir()、is_link()... ) return false 尽管符号链接仍然存在。

如果损坏的符号链接最初是针对目录的,这会变得更加麻烦。我可以在符号链接路径上执行 file_put_contents() ,这会在原始目标目录路径上创建一个文件。这是非常不幸和不可预测的。

所以我的问题是:有没有办法在 Windows 7 环境中检测 PHP 中损坏的符号链接?我需要任何可能的体面解决方案( exec() 或类似的东西)。生产服务器是 运行 标准 LAMP 配置,可以正常工作。

我正在使用 XAMPP 和 PHP 5.5.3。

示例脚本:

$dir_path = __DIR__ . '/temporary_directory';
$link_path = __DIR__ . '/broken_symlink';
mkdir($dir_path);
symlink($dir_path, $link_path);
rmdir($dir_path);

echo 'file_exists(): ';
var_dump(file_exists($link_path));// false
echo "<br>\n";

echo 'is_file(): ';
var_dump(is_file($link_path));// false
echo "<br>\n";

echo 'is_dir(): ';
var_dump(is_dir($link_path));// false
echo "<br>\n";

echo 'is_link(): ';
var_dump(is_link($link_path));// false
echo "<br>\n";

echo 'readlink(): ';
var_dump(readlink($link_path));// false
echo "<br>\n";

// Now it is possible to create file:

file_put_contents($link_path, '');

// which creates a new file on the $dir_path. That makes the symlink somewhat hybrid as Windows Explorer thinks it is a directory symlink but it points to a file (so it's unable to resolve it).

我想出了一个解决办法。我对此并不完全满意,但它应该有效。

function is_link_on_windows($path) {
    $parent_dir_path = dirname($path);

    if (false === file_exists($parent_dir_path)) return false;

    $filename_regex = preg_quote(substr($path, strlen($parent_dir_path) + 1), '#');

    // DOS' dir command is unlike PHP sensitive about the path format.
    $parent_dir_path = realpath($parent_dir_path);
    exec('dir ' . $parent_dir_path, $dir_output);

    foreach ($dir_output as $line) {
        // Symlink's entry should look like this:
        // broken_symlink [C:\xampp\htdocs\symlink_test\temporary_directory]
        // where the part within square brackets is the symlink's target.
        if (preg_match('#\s' . $filename_regex . '\s\[[^\]]+\]#', $line)) {
            return true;
        }
    }

    return false;
}