今年三月份去腾讯面试过。笔试题有一题是,PHP 中的 is_writable 函数不可信,请写一个函数来替代这个函数。
拿到这个题目,我便想起之前读 CI 框架实现的时候看到 CI 里面写了个 is_really_writable 函数,当时没有注意,只看这个函数的功能,至于 CI 为什么要自己实现一个 is_writable 函数的功能没有去深究。当时,我的解题思路是这样的,既然 is_writable 不可信,那么我就实实在在的尝试去那个文件夹里进行写入,如果可以写则返回 true,否则返回 false,思路是对的,但代码并不是完全无误,有瑕疵。
回到家后我便开始搜索,同时重新仔细查看 CI 的那个函数实现以及说明,原来是因为在 Windows 服务器上如果一个文件夹有只读属性,is_writable 这个函数还是会返回 true,而实际上 php 是不能进行写入的,另外一种情况是,在 unix 服务器上,如果 safe_mode 选项打开,那么 is_writable 函数也是不可信的。
这个事情提醒我,当遇到一个问题时,一定要追查到底,搞明白它的原理以及那样做的理由。
附上 CI 里面 is_really_writable 函数的实现:
Default
function is_really_writable($file)
{
// If we're on a Unix server with safe_mode off we call is_writable
if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == false) {
return is_writable($file);
}
// For windows servers and safe_mode "on" installations we'll actually
// write a file then read it. Bah...
if (is_dir($file)) {
$file = rtrim($file, '/') . '/' . md5(mt_rand(1, 100) . mt_rand(1, 100));
if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === false) {
return false;
}
fclose($fp);
@chmod($file, DIR_WRITE_MODE);
@unlink($file);
return true;
} elseif (!is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === false) {
return false;
}
fclose($fp);
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
functionis_really_writable($file)
{
// If we're on a Unix server with safe_mode off we call is_writable
if(DIRECTORY_SEPARATOR=='/'AND@ini_get("safe_mode")==false){
returnis_writable($file);
}
// For windows servers and safe_mode "on" installations we'll actually
// write a file then read it. Bah...
if(is_dir($file)){
$file=rtrim($file,'/').'/'.md5(mt_rand(1,100).mt_rand(1,100));
if(($fp=@fopen($file,FOPEN_WRITE_CREATE))===false){
returnfalse;
}
fclose($fp);
@chmod($file,DIR_WRITE_MODE);
@unlink($file);
returntrue;
}elseif(!is_file($file)OR($fp=@fopen($file,FOPEN_WRITE_CREATE))===false){
returnfalse;
}
fclose($fp);
returntrue;
}