PHP在保存文件时捕获错误

时间:2012-05-01 23:53:05

标签: php file-io exception-handling error-handling

所以....我正在写一个小函数,它返回一个文件是否已被写入,以及为什么不是......之类的东西:

array (
    'success' => false,               // easy, by checking function result
    'reason' => 'Permission denied'   // how am I supposed to find this???
)

也许我错过了一些东西但是在保存文件失败时我似乎找不到捕获任何错误消息 的方法。

我的第一个想法是使用输出缓冲来捕获错误,但它超出范围,容易出错并且出现大量黑客(即,不同类型的错误可能会干扰)。

通常,这对于OOP样式的异常处理来说是一个很好的工作,除了file_put_contentsf*函数不会抛出任何异常。

似乎SplFileObject完成了这项工作......但有一个例外;它是一个基于行的类,not suitable for reading binary files

有什么建议吗?

PS:叫我懒,但我不相信我的代码应该检查所有特殊情况(写入权限,无法访问的驱动器,错误的路径等)。

2 个答案:

答案 0 :(得分:2)

你提出的声音在表面上是正确的,有一个总体API,它将执行将文件写入文件系统的基本操作。但是,我认为PHP开发人员会让我们将我们的应用程序需求放在一起,因为它们确实为我们提供了自己完成的基本组件。

以下是我用于编写操作文件的File::write方法的片段:

$fileInfo = new SplFileInfo($fileUri);

if (!is_dir($fileInfo->getPath())) {
    // I have some proprietary stuff here but you get the idea        
}

$file = new SplFileObject($fileUri, $mode);

if (!$file->flock(LOCK_EX | LOCK_NB)) {
    throw new Exception(sprintf('Unable to obtain lock on file: (%s)', $fileUri));
} 

elseif (!$file->fwrite($content)) {
    throw new Exception(sprintf('Unable to write content to file: (%s)... to (%s)', substr($content,0,25), $fileUri));
}

elseif (!$file->flock(LOCK_UN)) {
    throw new Exception(sprintf('Unable to remove lock on file: (%s)', $fileUri));
}

elseif (!@chmod($fileUri, $filePerms)) {
    throw new Exception(sprintf('Unable to chmod: (%s) to (%s)', $fileUri, $filePerms));
}

这些只是您可以测试的边缘情况的几个示例,如果您需要测试“驱动器是否已连接”,您可以调用is_writable。因此,您只需将其添加到检查列表中,并使用对您的应用程序有意义的消息进行响应。

然后,如果你想记录所说的错误,只需将调用代码包装在try / catch块中:

try {
    File::write($fileUri);
} catch (Exception $e) {
    error_log($e->getMessage);
}

答案 1 :(得分:1)

更新

@hakre刚刚给了我一个好主意。这是默认的FS实现,与我的系统不一致。

解决这个问题的方法是取消注册file://协议的默认流包装并注册我自己的包,这实际上会抛出异常。有了这个,我可以自由地使用file_put_contents()并同时捕获异常。

哦,我还可以确保自定义流包装器也是原子的(通过强制锁定)。


这是我到目前为止所提出的。当然,它需要真正的FS检查(驱动器/路径存在,权限等)。

    /**
     * Save file to source DSN.
     * @return boolean True on success, false on failure (see last_error for details). 
     */
    public function save(){
        $file = fopen($this->source, 'wb');
        if(!$file){
            $this->_last_error = 'Could not open file stream';
            return false;
        }
        if(!flock($file, LOCK_EX)){
            $this->_last_error = 'Could not lock file for writing';
            return false;
        }
        if(!ftruncate($file, 0)){
            $this->_last_error = 'Could not clear file';
            return false;
        }
        if(fwrite($file, $this->contents)===null){
            $this->_last_error = 'Could not write to file';
            return false;
        }
        if(!fflush($file)){
            $this->_last_error = 'Could not flush to file';
            return false;
        }
        if(!flock($file, LOCK_UN)){
            $this->_last_error = 'Could not unlock file';
            return false;
        }
        if(!fclose($file)){
            $this->_last_error = 'Could not close file';
            return false;
        }
        return true;
    }

非常冗长,而且与我想要的完全不同。通过FS检查,这可能会增加很多。

如果file_get_contents()首先编码,那么所有这些代码都可以实现,这真是遗憾。