所以....我正在编写一个小函数,它返回文件是否已写入,以及为什么不写入......像这样:
array (
'success' => false, // easy, by checking function result
'reason' => 'Permission denied' // how am I supposed to find this???
)
也许我错过了一些东西,但我似乎找不到一种方法来捕获保存文件失败时的任何错误消息。
我的第一个想法是使用输出缓冲来捕获错误,但它超出了范围,容易出错并且是一个巨大的黑客(即,不同类型的错误可能会干扰)。
通常,这对于 OOP 样式的异常处理来说是一个很好的工作,除了 file_put_contents
和 f*
函数不会引发任何异常。
看来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()
来实现。