保存文件时出现 PHP 捕获错误


PHP capture errors while saving file

所以....我正在编写一个小函数,它返回文件是否已写入,以及为什么不写入......像这样:

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

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

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

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

看来SplFileObject可以完成这项工作...除了一个例外;它是一个基于行的类,不适合读取二进制文件。

有什么建议吗?

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

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

以下是我用于文件编写操作的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);
}

更新

@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()来实现。