文档章节

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

曾建凯
 曾建凯
发布于 2016/12/10 19:11
字数 1214
阅读 2059
收藏 12
点赞 2
评论 27

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

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

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

实在是忍不住要吐槽一下,从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',
];

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

第一次测试

执行测试:

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

输出结果如下:

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

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

所以,一般到这里,会让我们得出一个想当然的结论: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#等等,多了去的选择。所以如果这个语言本身不可靠,那我真的应该考虑换一个语言了。

© 著作权归作者所有

共有 人打赏支持
曾建凯
粉丝 323
博文 57
码字总数 90297
作品 0
广州
技术主管
加载中

评论(27)

SimonXia
SimonXia
明显let_it_work程序有问题。根本没有走到if(xxx){}这个子句里面去
FlyingHail
FlyingHail

引用来自“FlyingHail”的评论

这个,看起来就是 realpath 不支持 phar 呗,或许算不上是bug吧

至于 ArrayObject,是不是应该这样写啊....
$arr = new ArrayObject(['item ' => 1]);
$arr['item '] += 1;

如果非得想 $arr->item += 1,要这样写:
$a = new ArrayObject(['item' => 1], ArrayObject::ARRAY_AS_PROPS);
$a->item += 1;

引用来自“曾建凯”的评论

如果非得想 $arr->item += 1,要这样写:
$a = new ArrayObject(['item' => 1], ArrayObject::ARRAY_AS_PROPS);
$a->item += 1;

这种写法也是不会触发到offsetSet和offsetGet的,必须$a['item'] += 1方可。
这个从设计上看,也是正常的啊,因为 $a->item 这根本不是操作数组,是直接操作 object 的 property,自然没法触发 offsetSet 和 offsetGet
digmeup
digmeup
博主态度很不错,赞一个
曾建凯
曾建凯

引用来自“justintung”的评论

file_exists都是和stream wrapper 相关的,会在/path,默认认为是file:///path ,realpath和这个无关;
感觉发现新大陆一样,写这么多
受教了,stream_get_wrappers里注册了phar,使得file_exists支持。
曾建凯
曾建凯

引用来自“FlyingHail”的评论

这个,看起来就是 realpath 不支持 phar 呗,或许算不上是bug吧

至于 ArrayObject,是不是应该这样写啊....
$arr = new ArrayObject(['item ' => 1]);
$arr['item '] += 1;

如果非得想 $arr->item += 1,要这样写:
$a = new ArrayObject(['item' => 1], ArrayObject::ARRAY_AS_PROPS);
$a->item += 1;
如果非得想 $arr->item += 1,要这样写:
$a = new ArrayObject(['item' => 1], ArrayObject::ARRAY_AS_PROPS);
$a->item += 1;

这种写法也是不会触发到offsetSet和offsetGet的,必须$a['item'] += 1方可。
FlyingHail
FlyingHail
这个,看起来就是 realpath 不支持 phar 呗,或许算不上是bug吧

至于 ArrayObject,是不是应该这样写啊....
$arr = new ArrayObject(['item ' => 1]);
$arr['item '] += 1;

如果非得想 $arr->item += 1,要这样写:
$a = new ArrayObject(['item' => 1], ArrayObject::ARRAY_AS_PROPS);
$a->item += 1;
ma_ming-nj
ma_ming-nj
看的我都懵逼了,这根本不是bug,人家为啥要改,你第二次代码直接返回的参数,就在那里叫说不对。
不是simaguo
不是simaguo
leo108 说的对,第二个逻辑有问题
BlinkCG
BlinkCG
看的尴尬癌都犯了。赶紧换语言吧。
wangdmeng
wangdmeng
简直牛逼坏了
PHP7新特性快速一览。

2015.12.3发生了两件大事,PHP7问世了,Swift开源了。 最好的语言发布了新的版本,一个划时代的大版本:PHP7。 PHP7修复了大量BUG,新增了功能和语法糖。这些改动涉及到了核心包、GD库、PDO...

李轩Lane ⋅ 2015/12/04 ⋅ 4

TXR 89 发布,文本抽取的模式匹配语言

TXR 是一个新的用于文本抽取的模式匹配语言,用于替换 awk 和 perl,提供基于模板的全文匹配。 TXR 89 发布,此版本修复了 eql 函数对浮点数值操作不当的问题;修复了T get-line,get-char 和...

oschina ⋅ 2014/05/12 ⋅ 2

PHP的Realpath Cache

PHP的缓存有很多种,包括输出缓冲(ob系列函数),opcode缓存(APC,eAccelerator,XCache等扩展实现),这些大家已经很熟悉了,接下来介绍一下一个不太被人注意的PHP缓存机制:realpath_cache。 ...

clearchen ⋅ 2012/08/15 ⋅ 0

SQLite 3.7.16.1 发布

SQLite 3.7.16.1 发布, 主要以下内容: 修复 3.7.15 版本因为优化 ORDER BY 而导致的bug. Ticket a179fe7465. 修复一个长期存在的在CAST表达式中会将UTF16字符(即使高位不为0)当作数字的bug. ...

chai2010 ⋅ 2013/03/29 ⋅ 2

FISH Shell 2.4.0 发布,动态 SHELL

Fish shell 24.0 发布了,FISH Shell 是一个动态 SHELL,提供命令自动完成、智能脚本、帮助信息自动完成和基于 web 配置等特性。 更新内容: The fishrealpath builtin has been renamed to...

leolovenet ⋅ 2016/11/09 ⋅ 7

纵使有花兼明月何堪无酒亦无人/xukey

xukey base on ukey 支持PHP7 请选择PHP7分支,修复内存泄露的bug Based on the Twitter Snowflake algorithm PHP unique ID generator functions list: string ukeynextid(void); Get the n......

纵使有花兼明月何堪无酒亦无人 ⋅ 2016/05/05 ⋅ 0

Haxe 3.4.3 发布,基于高级编程语言的工具包

Haxe 3.4.3 已发布。Haxe 原定位是一门跨平台编程语言,可胜任开发原生 iOS、Android 应用、网页,可应用于服务器和个人桌面。调整后定位为一款基于现代、高级、严格类型化的编程语言、交叉编...

王练 ⋅ 2017/09/16 ⋅ 1

PHP 回调后门笔记

在php中 存在回调函数参数的函数,都有可能作为后门 比如arraymap , arrayfilter 这类数组操作的回调函数,但是在安全狗下会被杀掉 但是uasort 这类函数不会被杀掉。 php5.4.8之后的assert ...

wly1992 ⋅ 2017/05/16 ⋅ 0

THINKPHP下functions.php

在think.php的common文件下有一个公共函数库文件functions.php的352行有这么一个函数 // 区分大小写的文件存在判断 function fileexistscase($filename) { if (is_file($filename)) { if (I...

walden ⋅ 2012/03/11 ⋅ 4

Swoole-1.8.9 发布,增加命名空间别名

PHP的异步、并行、高性能网络通信引擎Swoole 已发布 1.8.9 版本。此版本增加了命名空间别名,可在PHP代码中同时使用namespace类名或下划线风格类名。另外此版本修复了近期发现的一些BUG,其中...

matyhtf ⋅ 2016/08/18 ⋅ 27

没有更多内容

加载失败,请刷新页面

加载更多

下一页

使用 vue-cli 搭建项目

vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli 一、 安装 node.js 首先需要安装node环境,可以直接到中...

初学者的优化 ⋅ 18分钟前 ⋅ 0

设计模式 之 享元模式

设计模式 之 享元模式 定义 使用共享技术来有效地支持大量细粒度对象的复用 关键点:防止类多次创建,造成内存溢出; 使用享元模式来将内部状态与外部状态进行分离,在循环创建对象的环境下,...

GMarshal ⋅ 34分钟前 ⋅ 0

SpringBoot集成Druid的最简单的小示例

参考网页 https://blog.csdn.net/king_is_everyone/article/details/53098350 建立maven工程 Pom文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM......

karma123 ⋅ 今天 ⋅ 0

Java虚拟机基本结构的简单记忆

Java堆:一般是放置实例化的对象的地方,堆分新生代和老年代空间,不断未被回收的对象越老,被放入老年代空间。分配最大堆空间:-Xmx 分配初始堆空间:-Xms,分配新生代空间:-Xmn,新生代的大小一...

算法之名 ⋅ 今天 ⋅ 0

OSChina 周日乱弹 —— 这么好的姑娘都不要了啊

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @TigaPile :分享曾惜的单曲《讲真的》 《讲真的》- 曾惜 手机党少年们想听歌,请使劲儿戳(这里) @首席搬砖工程师 :怎样约女孩子出来吃饭,...

小小编辑 ⋅ 今天 ⋅ 8

Jenkins实践3 之脚本

#!/bin/sh# export PROJ_PATH=项目路径# export TOMCAT_PATH=tomcat路径killTomcat(){pid=`ps -ef | grep tomcat | grep java|awk '{print $2}'`echo "tom...

晨猫 ⋅ 今天 ⋅ 0

Spring Bean的生命周期

前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: Spring 只帮我们管理单例模...

素雷 ⋅ 今天 ⋅ 0

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 今天 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部