PHP在保存文件时捕获错误
所以。。。。我正在写一个小函数,它返回文件是否被写入,以及为什么没有写入的原因。。。。比如:PHP在保存文件时捕获错误,php,file-io,exception-handling,error-handling,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??? ) 也许我遗漏了什么,但我似乎找不到一种方法来捕获保存文件失败时的任何错误消
array (
'success' => false, // easy, by checking function result
'reason' => 'Permission denied' // how am I supposed to find this???
)
也许我遗漏了什么,但我似乎找不到一种方法来捕获保存文件失败时的任何错误消息
我的第一个想法是使用输出缓冲来捕获错误,但它荒谬地超出了范围,容易出错,而且是一个巨大的黑客攻击(即,不同类型的错误可能会干扰)
通常,这对于OOP风格的异常处理来说是一项很好的工作,除了
文件内容
和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));
}
这些只是您可以测试的边缘情况的几个示例,如果您需要测试“驱动器是否已连接”,您可以调用。因此,您可以将其添加到检查列表中,并用对您的应用程序有意义的消息进行响应
然后,如果要记录所述错误,只需将调用代码包装在try/catch块中:
try {
File::write($fileUri);
} catch (Exception $e) {
error_log($e->getMessage);
}
更新
@哈克雷刚刚给了我一个好主意。默认的FS实现与我的系统不一致
解决此问题的一种方法是取消注册file://
协议的默认流包装器,并注册我自己的,这实际上会引发异常。有了它,我就可以自由地使用file\u put\u contents()
,同时捕获异常
哦,我还可以确保自定义流包装器也是原子的(通过强制锁定)
这是我到目前为止想到的。当然,它需要真正的FS检查(驱动器/路径存在、权限等) 这是相当冗长的,真的不像我想要的。通过FS检查,这可能会增加一点
如果一开始编码正确,那么所有这些代码都可以通过
file\u get\u contents()
实现。file\u put\u contents
和其他人在失败时返回false
,因此您可以检查它,然后抛出自己的异常。@tandu我需要原因,我为什么要向FS查询失败原因的信息?另外,由于它不是原子的,所以原因可能会在写入文件和检查错误的时间之间发生变化(虽然是毫秒,但不好的做法是不好的)。同样,这还不够。它失败的原因有一百万个(或者可写
返回false),我需要这些原因。明白我的意思吗?@Christian:Define unrelated,你注册你的范围,然后注销。更新:也请看这里,这可能会起作用:@Christian:听起来像是与操作相关的错误。我使用的是SplFileObject(除了例外),它工作得很好,直到我意识到它只适用于文本文件。我已经在我的问题中谈到了这一点。我读了这个问题,有几个部分是我的答案的基础。也许发布您正在使用的代码可以减少任何重复。至于测试是否写入了二进制图像,为什么不在你的应用程序移动、调整大小、上传文件后检查is_文件
?我不确定我是否理解你的后一个问题。由于file\u put\u contents()
的结果(或f*
FS函数的集体结果),我已经知道操作是否成功。你的帖子回答了我的问题。基本上我发布的内容,我看不出有任何问题。您可以通过添加和删除复选框来减少冗长。如果你选择自定义默认的FS功能,发布你的发现,我总是想学习一种新的剥猫方法。我不确定我对这两个答案是否满意,所以寻找更好答案的游戏仍然在进行(同时,我在值得的地方投了更高的票)。我喜欢这个问题,对结果感兴趣,如果有更好的解决方案出现,我可能不得不更新我的API。谢谢
/**
* 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;
}