PHP并发下安全读写文件函数

小畅 625 次浏览 0

众所周知,在高并发的状态下,直接使用 PHP 读写同一个文件时,可能会导致文件内容丢失,于是乎就需要额外的代码来解决这个问题。大致的思路是先使用 flock 函数对原文件进行锁死,再来读写。

下面的这个函数是从大名鼎鼎的可道云的代码中找到的。太开心可道云相信大家都不会陌生,它是一个无数据库的程序,因此配置存储全都是靠这个函数完成的,所以这段代码的安全性和普适性绝对毋庸置疑,可以放心的用于项目中(注意尽量保留原作者的版权信息就行了)。

代码的原版位于可道云的 /app/function/file.function.php 第 729 行左右。原版代码的逻辑是 写文件时如果原文件不存在,则直接返回 false,我把这一部分稍微修改了一下,改成了 如果目标文件不存在,则创建文件并写入。dog13

不多说了,全部的代码如下:

PHP
  1. <?php
  2. /**
  3. * @link http://kodcloud.com/
  4. * @author warlee | e-mail:kodcloud@qq.com
  5. * @copyright warlee 2014.(Shanghai)Co.,Ltd
  6. * @license http://kodcloud.com/tools/license/license.txt
  7. */
  8. /**
  9. * 安全读取文件,避免并发下读取数据为空
  10. *
  11. * @param $file 要读取的文件路径
  12. * @param $timeout 读取超时时间
  13. * @return 读取到的文件内容 | false - 读取失败
  14. */
  15. function file_read_safe($file, $timeout = 5) {
  16. if (!$file || !file_exists($file)) return false;
  17. $fp = @fopen($file, 'r');
  18. if (!$fp) return false;
  19. $startTime = microtime(true);
  20. // 在指定时间内完成对文件的独占锁定
  21. do {
  22. $locked = flock($fp, LOCK_EX | LOCK_NB);
  23. if (!$locked) {
  24. usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试
  25. }
  26. }
  27. while ((!$locked) && ((microtime(true) - $startTime) < $timeout));
  28. if ($locked && filesize($file) >= 0) {
  29. $result = @fread($fp, filesize($file));
  30. flock($fp, LOCK_UN);
  31. fclose($fp);
  32. if (filesize($file) == 0) {
  33. return '';
  34. }
  35. return $result;
  36. } else {
  37. flock($fp, LOCK_UN);
  38. fclose($fp);
  39. return false;
  40. }
  41. }
  42. /**
  43. * 安全写文件,避免并发下写入数据为空
  44. *
  45. * @param $file 要写入的文件路径
  46. * @param $buffer 要写入的文件二进制流(文件内容)
  47. * @param $timeout 写入超时时间
  48. * @return 写入的字符数 | false - 写入失败
  49. */
  50. function file_write_safe($file, $buffer, $timeout = 5) {
  51. clearstatcache();
  52. if (strlen($file) == 0 || !$file) return false;
  53. // 文件不存在则创建
  54. if (!file_exists($file)) {
  55. @file_put_contents($file, '');
  56. }
  57. if(!is_writeable($file)) return false; // 不可写
  58. // 在指定时间内完成对文件的独占锁定
  59. $fp = fopen($file, 'r+');
  60. $startTime = microtime(true);
  61. do {
  62. $locked = flock($fp, LOCK_EX);
  63. if (!$locked) {
  64. usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试
  65. }
  66. }
  67. while ((!$locked) && ((microtime(true) - $startTime) < $timeout));
  68. if ($locked) {
  69. $tempFile = $file.'.temp';
  70. $result = file_put_contents($tempFile, $buffer, LOCK_EX);
  71. if (!$result || !file_exists($tempFile)) {
  72. flock($fp, LOCK_UN);
  73. fclose($fp);
  74. return false;
  75. }
  76. @unlink($tempFile);
  77. ftruncate($fp, 0);
  78. rewind($fp);
  79. $result = fwrite($fp, $buffer);
  80. flock($fp, LOCK_UN);
  81. fclose($fp);
  82. clearstatcache();
  83. return $result;
  84. } else {
  85. flock($fp, LOCK_UN);
  86. fclose($fp);
  87. return false;
  88. }
  89. }
复制 文本 高亮

后记

可道云的代码里其实还有大量的封装好的非常好用的函数,仔细研究研究绝对受益匪浅。

发表评论 取消回复
表情 图片 链接 代码

分享