PHP fopen无法打开文件但仍然可以写入文件。

3
这真让我发疯。我有一个标准的写日志文件例程,它一直运作得非常好,以至于我已经快忘记代码中有什么了。把它转移到新项目中,作了一个更改,但它的行为非常奇怪。错误消息显示fopen()无法打开日志文件,然而日志文件中的条目仍然被写入其中。虽然尽管出现错误但它似乎仍在工作,但我不喜欢任何形式的警告或错误,因此我不打算忽略这个错误。
我所做的更改是关于如何设置$_SESSION ['base_dir_prefix']的。在之前的项目中,在主页的上下文中设置了绝对路径到该目录。这个方法有一个弱点,就是如果不通过index.php就无法直接进入页面。但在这个项目中,我不能这样做,所以我将其设置为相对路径——例如, $_SESSION ['base_dir_prefix'] = "../" ; 就我所看到的,前缀已经正确设置了。事实上,就我所看到的,一切都设置正确了,但显然还有一个未知问题需要另一双眼睛去找!
我的最大问题之一是,如果fopen()无法打开文件,那么为什么它仍可以被写入? 展示 1 - 实际写日志函数的源代码。请注意,echo语句仅用于调试目的,不是正常执行的一部分。
function writelog($str=" ")
{
    if (isset($_SESSION['base_dir_prefix']))
    {
        $prepend = $_SESSION['base_dir_prefix'] ;
    }
    else
    {
        $prepend = $_SERVER['DOCUMENT_ROOT'] ;
    }

    if ( substr($prepend, -1, 1) <>  DIRECTORY_SEPARATOR )
    {
        $prepend .= DIRECTORY_SEPARATOR ;
    }
    $fname = $prepend."log/log.txt" ;
    $mode = "a" ;

    echo getcwd(). "\n" ;           // debug code
    echo $fname." Files exists?:".file_exists($fname) ."\n";
    echo $str."\n" ;

    $fp = fopen($fname, $mode) ;

    fwrite( $fp, date("Y-m-d H:i:s").": " ) ;
    fwrite( $fp, $str."\n" ) ;
    fclose( $fp ) ;
}

图2 - 调用函数的代码

    $u->writelog ("SA OLU entering Option List Users function") ;
    $u->writelog ("Current dir:".getcwd()." ") ;

    $usr_list = array(array( "None", "None") ); 
    $sc->user_list($usr_list) ; 
    $i = 0 ; 
    $res = "" ;
    $actv_flag = strtoupper($active) ;

    if ($usr_list[0][0] != "None" ) 
    { 
        while ($i < count($usr_list)) 
        {
            if ((bool)$usr_list[$i][2] == true && $actv_flag == "YES")   //only active users
            {
                $i++ ;
                continue ;
            }
            $res .= sprintf( "<option value=\"%s\"> %s (%s) </option> \n", 
                             $usr_list[$i][0], 
                             $usr_list[$i][1], 
                             $usr_list[$i][0]
                           ); 
            $i++ ; 
        } 
     } else 
     { 
            $res = sprintf( "<option value=\"no_recs\"> No Program Records </option> \n" ) ; 
     }
     $u->writelog ("SA OLU leaving Option List Users function") ;

图3 - 日志文件本身。由图1生成的条目。请注意,此文件的时间戳比实际时间晚1小时。我不确定原因,但由于只有我一个人查看此文件,所以并没有过度担忧。

2014-11-21 16:29:58: SA OLU entering Option List Users function
2014-11-21 16:29:58: Current dir:/srv/www/dev/gillies/security
2014-11-21 16:29:58: SA OLU leaving Option List Users function
2014-11-21 16:29:58: SA OLU entering Option List Users function
2014-11-21 16:29:58: Current dir:/srv/www/dev/gillies/security
2014-11-21 16:29:58: SA OLU leaving Option List Users function
2014-11-21 16:29:58: SA OLU entering Option List Users function
2014-11-21 16:29:58: Current dir:/srv/www/dev/gillies/security
2014-11-21 16:29:58: SA OLU leaving Option List Users function

附录4 - Apache错误日志文件

[Fri Nov 21 15:29:25.047732 2014] [:error] [pid 20588] [client 192.168.0.19:62116] PHP Warning:  fwrite() expects parameter 1 to be resource, boolean given in /srv/www/dev/gillies/php/utilities.php on line 72
[Fri Nov 21 15:29:25.047746 2014] [:error] [pid 20588] [client 192.168.0.19:62116] PHP Warning:  fclose() expects parameter 1 to be resource, boolean given in /srv/www/dev/gillies/php/utilities.php on line 73
[Fri Nov 21 15:29:58.721627 2014] [:error] [pid 17084] [client 192.168.0.19:62141] PHP Warning:  fopen(../log/log.txt): failed to open stream: No such file or directory in /srv/www/dev/gillies/php/utilities.php on line 69
[Fri Nov 21 15:29:58.721698 2014] [:error] [pid 17084] [client 192.168.0.19:62141] PHP Warning:  fwrite() expects parameter 1 to be resource, boolean given in /srv/www/dev/gillies/php/utilities.php on line 71
[Fri Nov 21 15:29:58.721711 2014] [:error] [pid 17084] [client 192.168.0.19:62141] PHP Warning:  fwrite() expects parameter 1 to be resource, boolean given in /srv/www/dev/gillies/php/utilities.php on line 72
[Fri Nov 21 15:29:58.721720 2014] [:error] [pid 17084] [client 192.168.0.19:62141] PHP Warning:  fclose() expects parameter 1 to be resource, boolean given in /srv/www/dev/gillies/php/utilities.php on line 73

展示5 - 生成的HTML代码片段,显示了回显结果。这些条目是由展示2创建的。请注意,此仅用于诊断目的,不属于生产文件的一部分。

                    </h4><select name="users" size="5">
                      /srv/www/dev/gillies/security
../log/log.txt Files exists?:1
SA OLU entering Option List Users function
/srv/www/dev/gillies/security
../log/log.txt Files exists?:1
Current dir:/srv/www/dev/gillies/security 
/srv/www/dev/gillies/security
../log/log.txt Files exists?:1
SA OLU leaving Option List Users function
        <option value="loginid"> name (loginid) </option>

图6 - 日志文件的目录结构

darryl@thedoctor:/srv/www/dev/gillies/log$ pwd
/srv/www/dev/gillies/log
darryl@thedoctor:/srv/www/dev/gillies/log$ ls -l
total 8
-rw-rw-rw- 1 www-data www-data 4743 Nov 21 15:33 log.txt

有线索的吗?谢谢。

在你的代码中,某处将$fname设置为布尔值。你需要通过var_dump()进行调试,查看它从字符串变成布尔值的位置。 - Darren
Darren,感谢你的想法,但我不认为会发生这种情况。如果您查看展示1,$fname在fopen使用之前立即被回显。展示5显示它不是布尔值,并且还验证了文件$fname确实存在。 - Darryl Severn
我以前从未尝试过相对路径 - 也许这就是问题所在。但我仍然无法理解fopen不打开文件但文件却被写入的情况。 - Darryl Severn
你怎么知道它没有打开文件?你可以尝试像这样的代码:if(!fopen($fname, $mode)) {echo 'fail';} else {...do the writing stuff....} - Darren
我认为它无法打开是因为在Apache日志文件中有这样一行:“PHP警告:fopen(../log/log.txt):无法打开流:没有那个文件或目录”。 - Darryl Severn
显示剩余4条评论
1个回答

1
我已经找到了答案,它在提供给fopen()的相对路径中。我重写了代码以创建一个绝对而不是相对的前缀,所有的错误都消失了。我需要进行一些字符串操作来获取正确的目录,新代码现在看起来像这样:
$here = getcwd() ;
$posn = strrpos($here, "/") ;
if($posn === false) {
    $_SESSION['base_dir_prefix'] = "../" ;
}
else {
    $_SESSION['base_dir_prefix'] = substr($here, 0, $posn) ;
}

感谢你的建议,达伦。我一直在打转,试图弄清楚还有什么其他方法可尝试。
新代码获取层次结构中第二级目录的绝对路径,并将路径向后修剪一个级别,以便它指向文档根目录。
对于那些可能想知道为什么要费这么大劲去获取目录的人来说,这使得代码完全可重定位。不能保证文档根目录相对于Web服务器文档根目录的级别与网站在开发、测试、验收和生产阶段中移动的级别相同。这允许将网站放置在目录层次结构中的任何位置,而无需更改对文件等的引用。

你也可以使用dirname(__FILE__)。 - bksi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接