PHP7 realpath函数一个长期存在的bug

本文最后结论:PHP的realpath函数不支持phar文件。通过网友指出,是我脑壳发热,自觉得是的 let_it_work函数,觉得生效,其实里面存在很是致命的逻辑错误,我记住教训了php

为了留下的记录,文章内容一字不改,只在 let_it_work 函数那里注释了一下。php7

这事情教育我,仍是得要单元测试,这种想固然错误太明显了。函数

实在是忍不住要吐槽一下,从7.0.0到7.0.4的时候,我一直在看这个bug,并且也发去php issues了,已经说修复了,可是显然并无修复。后来忙,就没管这个问题了,但是到今天,都7.0.14了,7.1.0都发布了,仍是没修复啊!单元测试

phar包

假定我有一个phar包,包内结构以下:测试

$paths = [
	'phar://phar_test.phar/hello',
	'phar://phar_test.phar/hello/a.php',
];

上述的这个路径,是存在的。spa

第一次测试

执行测试:code

foreach ($paths as $path) {
	var_dump(file_exists($path)); // return true
	var_dump(realpath($path)); // return false
}

输出结果以下:接口

若是用file_exists检查,那么他是返回true的,文件是存在的。内存

用realpath检查,他返回了false,就是返回真实路径的时候,他没法返回相对应的真实路径。get

因此,通常到这里,会让咱们得出一个想固然的结论:realpath显然不支持phar包。

第二次测试

问题并无结束,显然realpath是支持phar的,此次咱们加一个函数:

function let_it_work(string $path)
{
	$realPath = realpath($path);
	if ($realPath !== false) {
		$path = $realPath;
	}
    // 这里我想固然了
    // 应该改成 return false
	return $path;
}

这个函数其实没啥特别,可是他却能让咱们获得想要的结果。

执行下列测试程序:

foreach ($paths as $path) {
	var_dump(file_exists($path)); // return true
	var_dump(realpath($path)); // return false
	var_dump(let_it_work($path)); // 输出 "phar://phar_test.phar/hello" 和 "phar://phar_test.phar/hello/a.php"
}

咱们会获得以下的结果:

显然realpath函数仍是生效了。否则是不会获得真实路径的。

第三次测试

那么是否是给realpath包一层函数,就能获得咱们想要的结果呢?那我再加一个函数:

function realpath2(string $path)
{
	return realpath($path);
}

执行如下测试:

foreach ($paths as $path) {
	var_dump(file_exists($path)); // return true
	var_dump(realpath($path)); // return false
	var_dump(let_it_work($path)); // 输出 "phar://phar_test.phar/hello" 和 "phar://phar_test.phar/hello/a.php"
	var_dump(realpath2($path)); // 和realpath返回结果同样
}

获得以下的结果:

显然包一层函数是不能解决问题的。

第四次测试

这个问题,并不止于realpath函数,全部获取realpath的函数,都存在这个问题。好比目录迭代器 DirectoryIterator,咱们再写一个函数:

function entry(DirectoryIterator $dir)
{
	foreach ($dir as $item) {
		$path = $item->getPathname();
		var_dump(file_exists($path)); // true
		var_dump($item->getRealPath()); // false
		var_dump(let_it_work($path)); // 这里返回的结果,是正确的
	}
}

entry(new DirectoryIterator('phar://phar_test.phar'));

这个测试很简单,就是传入一个目录迭代器,而后遍历目录下的内容,而后调用 getRealPath 方法以进行测试。

执行结果以下:

不出所料,getRealPath返回仍是返回无效的结果。

结论

其实这个问题看上去,并非一个很严重的问题,并且也有解决方案了,因此也没什么可抱怨的。

可是冷静分析一下let_it_work的函数,问题的关键在于:

if ($realPath !== false) {
	$path = $realPath;
}

$path变量是函数的参数传入的,可是到这里,我把他和false作了一次比较,而后又把他写入了另外一个变量的结果,这样就改变告终果。这里实际上是一个很严重的问题,就是变量的内存地址问题。也就是说,经过一些操做,就让一个变量的内存地址发生了变化,取回了正确的值。怎么想都以为很是诡异,莫名其妙。

这个问题从7.0.0发布的时候我就发现了,由于这个问题,这一年来我一直在认真考虑转Java仍是C#的问题。

固然,也许我能够经过一些内存跟踪的手段去明确这个问题的根本,但我实在懒得折腾了。

其实PHP7还有一些让我不太满意的问题,好比ArrayObject的问题,好比:ArrayObject->item += 1,是没法触发offsetSet和offsetGet接口的。这个可能不算bug,也许到php7,关闭了这个特性。

不管如何,我对PHP的态度是,我只是这个语言的使用者,若是你让我折腾C,我不如去写Go、Java、C#等等,多了去的选择。因此若是这个语言自己不可靠,那我真的应该考虑换一个语言了。

相关文章
相关标签/搜索