加入收藏 | 设为首页 | 会员中心 | 我要投稿 财气旺网 - 海宁网 (https://www.hainingwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

PHP技巧PHP文件系统基本操作类

发布时间:2022-07-08 08:16:41 所属栏目:编程 来源:互联网
导读:/* * * ===========Z================= * QQ:118824 * MSN:snakevil_@hotmail.com * HP:http://www.snakevil.com/ * ===========Z================= * */ /** * @]Class Name[= IO * @]Class URI[= System.IO * @]Purpose[= * 本类用于对文件系统的处理 * @]
  /*
 
  *
 
  * ===========Z=================
 
  * QQ:118824
 
  * MSN:snakevil_@hotmail.com
 
  * HP:http://www.snakevil.com/
 
  * ===========Z=================
 
  *
 
  */
 
  /**
 
  * @]Class Name[= IO
 
  * @]Class URI[= System.IO
 
  * @]Purpose[=
 
  * 本类用于对文件系统的处理
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]Version[= 1.1.1
 
  * @]Create[= 17:13 2004-3-25
 
  * @]Modifications[=
 
  * 4:04 2004-3-30
 
  * 修复 generate_path() 方法中存在的一些 BUG
 
  * 重新设计方法 no_comment()
 
  * 4:32 2004-3-29
 
  * 简化方法 list_dir() 的返回值
 
  * 增加方法 file_info() 获取文件或目录信息
 
  * 5:35 2004-3-28
 
  * 整理优化算法
 
  * 7:31 2004-3-27
 
  * 将错误处理抽象为基类
 
  * 增加方法 no_comment() 删除文件中 C 规范注释
 
  * @]See[=
 
  */
 
  class IO extends SnkClass {
 
  var result; // 操作返回结果,如方法返回值为 mixed,则成功操作结果可在此获得
 
  var exec_cmd; // 执行方法,暂时没应用到
 
  var exist_dir; // 创建目录时最后存在的目录,现用于 copy() 和 move()
 
  var buffer_size; // 文件读取缓冲区大小,根据服务应用规模和服务器配置修改,建议默认值
 
  function IO() {
 
  parent::SnkClass();
 
  this->result = array();
 
  this->exec_cmd = "";
 
  this->exist_dir = "";
 
  this->buffer_size = 8192;
 
  return this;
 
  }
 
  /**
 
  * @]Method Name[= list_dir()
 
  * @]Purpose[=
 
  * 读取指定目录内容,返回内容数组
 
  * @]Parameter[=
 
  * string dir_path 指定目录路径,默认为当前目录
 
  * @]Return[= mixed 错误返回 FALSE,否则返回
 
  * array(
 
  * array("name","location","type"),
 
  * ......
 
  * )
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function list_dir(path=".") {
 
  if (!is_dir(path)) return this->error_occur(0x000B, __FUNCTION__);
 
  if (!is_readable(path)) return this->error_occur(0x0002, path);
 
  dh = @opendir(path);
 
  result = array();
 
  path = realpath(path);
 
  if (path[strlen(path)-1]!=DIRECTORY_SEPARATOR) path .= DIRECTORY_SEPARATOR; // 保证目录绝对地址后带目录分隔符
 
  while (FALSE!==(fh=readdir(dh))) { // 使用 !== 防止处理名称为 0 或 FALSE 的文件、目录
 
  if (fh=="."||fh=="..") continue; // 忽略系统特定文件夹
 
  i = path.fh; // 获取绝对地址
 
  t = array(
 
  "name" => fh,
 
  "location" => i,
 
  "type" => is_file(i) ? 1 : (is_dir(i) ? 0 : -1)
 
  );
 
  result[] = t;
 
  }
 
  closedir(dh);
 
  unset(dh, fh, t, i);
 
  clearstatcache(); // 清除文件系统缓存
 
  return this->result = result;
 
  }
 
  /**
 
  * @]Method Name[= file_info()
 
  * @]Purpose[=
 
  * 获取指定文件或目录的属性
 
  * @]Parameter[=
 
  * string dir_path 指定目录路径,默认为当前目录
 
  * @]Return[= mixed 错误返回 FALSE,否则返回
 
  * array("name","location","type","size","access","change","modify","read","write"),
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function file_info(path=".") {
 
  path = realpath(path);
 
  if (!path) return this->error_occur(0x000A, __FUNCTION__);
 
  result = array(
 
  "name" => substr(path, strrpos(path, DIRECTORY_SEPARATOR) 1),
 
  "location" => path,
 
  "type" => is_file(path) ? 1 : (is_dir(path) ? 0 : -1),
 
  "size" => filesize(path),
 
  "access" => fileatime(path),
 
  "modify" => filemtime(path),
 
  "change" => filectime(path),
 
  "read" => is_readable(path),
 
  "write" => is_writeable(path)
 
  );
 
  clearstatcache();
 
  return this->result = result;
 
  }
 
  /**
 
  * @]Method Name[= seek_file()
 
  * @]Purpose[=
 
  * 根据正则表达式条件,在相应目录及给定层次的子目录中搜索匹配的文件、目录
 
  * @]Parameter[=
 
  * string pattern 兼容 PERL 标准的正则表达式指明搜索匹配要求,会添加 /^ /,默认为 .*
 
  * string path 进行搜索的目录路径,默认为当前路径
 
  * enum seesk_type 有 -1 0 1 三种可能值,0 仅文件夹,1 仅文件,-1 两者都包括,默认为 1
 
  * int sub_dir 搜索的子目录深度,指定目录不算,建议不要超过 5,默认为 0
 
  * limit limit 搜索结果限制,避免过度浪费系统资源,默认为 100
 
  * @]Return[= mixed 错误返回 FALSE,否则
 
  * array(
 
  * array(
 
  * "name","locate","type"
 
  * ),
 
  * ......
 
  * )
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function seek_file(pattern=".*", path=".", seek_type=1, sub_dir_level=0, limit=100) {
 
  /* 检查参数值 */
 
  is_error = seek_type!=1 && seek_type!=0 && seek_type!=-1;
 
  is_error = is_error && (!is_int(sub_dir_level) || sub_dir_level < 0);
 
  is_error = is_error && (!is_int(limit) || limit < 1);
 
  if (is_error) return this->error_occur(0x000B, __FUNCTION__);
 
  unset(is_error);
 
  result = array();
 
  /* array() == FALSE,所以需要使用 === */
 
  if (FALSE===i=this->list_dir(path)) return FALSE; // 如果不能列举目录,返回
 
  for (j=0,k=count(i);j<k;j ) {
 
  if (i[j]["type"]==-1) continue; // 对于非目录非文件项目,跳过
 
  if (i[j]["type"]==0&&sub_dir_level) { // 如果需要搜索下层目录
 
  if (FALSE===l=this->seek_file(pattern,i[j]["location"],seek_type,(sub_dir_level - 1),limit)) return FALSE;
 
  result = array_merge(result, l); // 将下层目录搜索结果添加
 
  }
 
  if (seek_type i[j]["type"]==1||!preg_match("/^".pattern."/", i[j]["name"])) continue; // 如果不搜索当前类型,跳过
 
  result[] = i[j];
 
  if (count(result)>=limit) { // 截去超过要求的长度,离开列举
 
  array_splice(result, limit);
 
  break;
 
  }
 
  }
 
  unset(i, j, k, l);
 
  return this->result = result;
 
  }
 
  /**
 
  * @]Method Name[= delete()
 
  * @]Purpose[=
 
  * 删除指定对象,文件或文件夹——包括内含子目录和文件的非空文件夹
 
  * @]Parameter[=
 
  * string path 指定要删除的内容路径,文件或目录均可
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function delete(path="") {
 
  path = realpath(path);
 
  if (!path) return this->error_occur(0x000A, __FUNCTION__);
 
  if (!is_dir(path)) {
 
  if (@unlink(path)) return TRUE; // 文件删除成功
 
  return this->error_occur(0x0004, path);
 
  } else {
 
  if (FALSE===i=this->list_dir(path)) return FALSE; // 不能列举目录
 
  for (j=0,k=count(i);j<k;j )
 
  if (!this->delete(i[j]["location"])) return FALSE; // 删除目录内容出错
 
  unset(i, j, k);
 
  return TRUE;
 
  }
 
  }
 
  /**
 
  * @]Method Name[= generate_path()
 
  * @]Purpose[=
 
  * 获取现有或不存在文件、目录的绝对地址
 
  * @]Parameter[=
 
  * string path 要获取地址的文件、目录现有相对、绝对地址
 
  * @]Return[= string 获得的地址
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function generate_path(path="") {
 
  i = "/"==DIRECTORY_SEPARATOR ? "//" : "/"; // 统一目录分割符
 
  path = str_replace(i, DIRECTORY_SEPARATOR, strval(path));
 
  if (path[strlen(path)-1]!=DIRECTORY_SEPARATOR) path .= DIRECTORY_SEPARATOR;
 
  i = strpos(path, DIRECTORY_SEPARATOR); // 获得路径中首个目录分割符的位置
 
  ext = substr(path, i 1);
 
  path = substr(path, 0, i 1);
 
  if (i=realpath(path)) path = i; // 得到基本路径
 
  else {
 
  ext = path.ext;
 
  path = realpath(".");
 
  }
 
  if (strlen(ext)) { // 对剩余内容处理
 
  ext = preg_replace("/[/:/*/?/"/</>/|]/", "", explode(DIRECTORY_SEPARATOR, ext));
 
  array_pop(ext);
 
  path = explode(DIRECTORY_SEPARATOR, path); // 建立目录层轴
 
  if (path[count(path)-1]=="") array_pop(path);
 
  while (count(ext)) {
 
  i = array_shift(ext);
 
  if (i==".."&&count(path)>1) array_pop(path);
 
  elseif (""!=str_replace(".", "", i)) path[] = i;
 
  }
 
  path = implode(DIRECTORY_SEPARATOR, path);
 
  }
 
  unset(ext, i);
 
  return path;
 
  }
 
  /**
 
  * @]Method Name[= make_dir()
 
  * @]Purpose[=
 
  * 建立任意文件夹,相对或绝对路径皆可,深层建立亦可
 
  * @]Parameter[=
 
  * string path 要建立的最终目录路径
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function make_dir(path="") {
 
  i = explode(DIRECTORY_SEPARATOR, this->generate_path(path)); // 生成目录路径
 
  path = array_shift(i);
 
  for (j=0,k=count(i);j<k;j ) {
 
  path .= DIRECTORY_SEPARATOR.i[j];
 
  if (!is_dir(path)) {
 
  if (this->exist_dir=="") this->exist_dir = path; // 记录最后存在的目录路径
 
  if (!@mkdir(path)) return this->error_occur(0x0003, substr(path, 0, strrpos(path, DIRECTORY_SEPARATOR)));
 
  }
 
  }
 
  if (this->exist_dir=="") this->exist_dir = path;
 
  return TRUE;
 
  }
 
  /**
 
  * @]Method Name[= verify_file()
 
  * @]Purpose[=
 
  * 使用 MD5 算法比较两个文件是否相同
 
  * @]Parameter[=
 
  * string src 源文件路径
 
  * string dst 目标文件路径
 
  * boolean interal 对于超过 1MB 文件,设置 FALSE 省去 MD5 检验步骤,减轻服务器负担
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function verify_file(src="", dst="", interal=TRUE) {
 
  if (!is_file(src)||!is_file(dst)) return this->error_occur(0x000B, __FUNCTION__);
 
  if (!is_readable(src)) return this->error_occur(0x0006, src);
 
  if (!is_readable(dst)) return this->error_occur(0x0006, dst);
 
  i = filesize(src);
 
  if (filesize(dst)!=i) { // 文件大小不等
 
  unset(i);
 
  return FALSE;
 
  }
 
  if (i>1024*1024*1024&&!interal) { // 对于 1MB 的文件,如果不要求精确检查,跳过
 
  unset(i);
 
  return TRUE;
 
  }
 
  unset(i);
 
  if (md5_file(src)!=md5_file(dst)) return FALSE; // 文件 MD5 效验不符合,内容不相同
 
  return TRUE;
 
  }
 
  /**
 
  * @]Method Name[= copy()
 
  * @]Purpose[=
 
  * 对任意文件夹、文件进行复制,相对或绝对路径皆可,文件复制完成后会进行效验,检查是否出错数据错误
 
  * @]Parameter[=
 
  * string src_path 指定要复制的源内容路径,文件或目录均可
 
  * string dst_path 指定要复制的目标内容路径,文件或目录均可,性质由 src_path 决定,可为 src_path 下层目录
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function copy(src="", dst="", sub=FALSE) {
 
  if (!src=realpath(src)) return this->error_occur(0x000B, __FUNCTION__);
 
  dst = this->generate_path(dst);
 
  if (is_dir(src)) { // 处理目录
 
  /*
 
  * 关于算法的说明:
 
  * 本来打算使用很简单的递归算法,遇神杀神,遇魔斩魔的,后来发现一个问题:如果目标路径
 
  * 为源路径的后代路径怎么办?这样算法会不停的探测下去…
 
  * 于是添加了 this->exist_dir 属性,用来记录这一情况下目标路径中存在的部分。于是新的问
 
  * 题出来了:如何保存这一属性?
 
  * 将整个功能整合到 this->copy() 方法中,那么必然需要在这个函数中记录 this->exist_dir
 
  * 的变化,于是乎需要另外的一个有效的方法来阻止每一次操作中对其的更改。
 
  * 作为变通,我使用的隐藏参数 sub,这个参数无论如何,只要算法不变,永远在参数表的最
 
  * 后一个。因此,方法开始变得不稳定,但这也没有办法,只能希望程序员自己不要故意破坏。
 
  * 在外部调用时,因为默认 FALSE,所以对 this->exist_dir 写。内部递归时,显性 TRUE,不
 
  * 该属性,保证有效性。
 
  */
 
  if (!is_readable(src)) return this->error_occur(0x0002, src);
 
  if (dst[strlen(dst)-1]!=DIRECTORY_SEPARATOR) dst .= DIRECTORY_SEPARATOR;
 
  if (TRUE===sub&&src==this->exist_dir) return TRUE; // 源路径为记录的目标路径
 
  if (TRUE!==sub) this->exist_dir = ""; // 记录创建目录前目标目录路径中存在的目录路径
 
  if (!this->make_dir(dst)) return FALSE; // 创建目录
 
  if (FALSE===i=this->list_dir(src)) return FALSE; // 读取目录出错
 
  for (j=0,k=count(i);j<k;j ) if (!this->copy(i[j]["location"], dst.i[j]["name"],TRUE)) return FALSE;
 
  unset(i, j, k);
 
  RETURN TRUE;
 
  } else {
 
  if (!is_readable(src)) return this->error_occur(0x0006, src);
 
  if (this->verify_file(src,dst)) return TRUE;
 
  if (!copy(src,dst)) return this->error_occur(0x0007, dst);
 
  if (!this->verify_file(src,dst)) {
 
  @unlink(dst); // 复制文件失败删除新文件
 
  return this->error_occur(0x0007, dst);
 
  }
 
  return TRUE;
 
  }
 
  }
 
  /**
 
  * @]Method Name[= move()
 
  * @]Purpose[=
 
  * 对任意文件夹、文件进行移动,相对或绝对路径皆可,文件移动完成后会进行效验,检查是否出错数据错误
 
  * @]Parameter[=
 
  * string src_path 指定要移动的源内容路径,文件或目录均可
 
  * string dst_path 指定要移动的目标内容路径,文件或目录均可,性质由 src_path 决定,可为 src_path 下层目录
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function move(src="", dst="", sub=FALSE) {
 
  if (!src=realpath(src)) return this->error_occur(0x000B, __FUNCTION__);
 
  dst = this->generate_path(dst);
 
  if (is_dir(src)) { // 处理目录
 
  if (!is_readable(src)) return this->error_occur(0x0002, src);
 
  if (dst[strlen(dst)-1]!=DIRECTORY_SEPARATOR) dst .= DIRECTORY_SEPARATOR;
 
  if (TRUE===sub&&src==this->exist_dir) return TRUE;
 
  if (TRUE!==sub) this->exist_dir = "";
 
  if (!this->make_dir(dst)) return FALSE;
 
  if (FALSE===i=this->list_dir(src)) return FALSE;
 
  for (j=0,k=count(i);j<k;j ) if (!this->move(i[j]["location"], dst.i[j]["name"],TRUE)) return FALSE;
 
  unset(i, j, k);
 
  if (FALSE===strpos(this->exist_dir,src))
 
  if (!@rmdir(src)) return this->error_occur(0x0004, src); // 对非目标目录的上层目录,删除
 
  return TRUE;
 
  } else {
 
  if (!is_readable(src)) return this->error_occur(0x0006, src);
 
  if (this->verify_file(src,dst)) return TRUE;
 
  if (!copy(src,dst)) return this->error_occur(0x0007, dst);
 
  if (!this->verify_file(src,dst)) {
 
  @unlink(dst);
 
  return this->error_occur(0x0007, dst);
 
  }
 
  if (!@unlink(src)) return this->error_occur(0x0006, src); // 删除源文件
 
  return TRUE;
 
  }
 
  }
 
  /**
 
  * @]Method Name[= no_comment()
 
  * @]Purpose[=
 
  * 清除文件中 C 规范的注释
 
  * @]Parameter[=
 
  * string path 指定要执行操作的文件
 
  * @]Return[= boolean 错误返回 FALSE,否则 TRUE
 
  * @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
 
  * @]See[=
 
  */
 
  function no_comment(path="") {
 
  if (!is_file(path)) return this->error_occur(0x000B, __FUNCTION__);
 
  if (!is_readable(path)) return this->error_occur(0x0006, path);
 
  if (!is_writeable(path)) return this->error_occur(0x0007, path);
 
  if (!th=tmpfile()) return this->error_occur(0x000C, path); // 创建临时文件
 
  fh = fopen(path, "r b");
 
  if (!flock(fh,LOCK_EX)) { // 锁定文件
 
  fclose(fh);
 
  unset(fh);
 
  return this->error_occur(0x0009, path);
 
  }
 
  fbuffer = fread(fh, this->buffer_size*2); // 文件读取缓冲区
 
  tbuffer = ""; // 临时文件缓冲区
 
  in_dq = in_sq = in_lc = in_bc = FALSE;
 
  while (fblen=strlen(fbuffer)) { // 处理原始数据
 
  fstats = feof(fh);
 
  for (i=0;i<fblen;i ) { // 分析文件内容
 
  if (!fstats&&i 5>fblen) break; // 文件未完全读取时临近缓冲区读取完成读取下一块文件内容
 
  j = substr(fbuffer, i, 2);
 
  k = j[0];
 
  if (j=="/*"&&!in_dq&&!in_sq&&!in_lc) { // 不在字符串和行注释中,块注释开始
 
  in_bc = TRUE;
 
  i ;
 
  } elseif (j=="*/"&&in_bc) { // 块注释结束
 
  in_bc = FALSE;
 
  i =2;
 
  } elseif (j=="//"&&!in_dq&&!in_sq&&!in_bc) { // 行注释开始
 
  in_lc = TRUE;
 
  i ;
 
  } elseif (in_lc&&(k=="/r"||k=="/n")) in_lc = FALSE; // 行注释结束
 
  elseif (j=="////"||j=="///""||j=="//'") { // 转义字符
 
  tbuffer .= j;
 
  i ;
 
  continue;
 
  } elseif (k=="/""&&!in_sq&&!in_bc&&!in_lc) in_dq = !in_dq; // 双引号字符串开始、结束
 
  elseif (k=="'"&&!in_dq&&!in_bc&&!in_lc) in_sq = !in_sq; // 单引号字符串开始、结束
 
  if (in_lc||in_bc) continue; // 在注释中,跳过
 
  tbuffer .= fbuffer[i];
 
  }
 
  fbuffer = substr(fbuffer, i); // 抛弃读取过的部分
 
  unset(i, j, k);
 
  if (!fstats) fbuffer .= fread(fh, this->buffer_size);
 
  if (fstats||strlen(tbuffer)>=this->buffer_size) { // 写入合法数据到临时文件
 
  if (!fwrite(th,tbuffer)) { // 写入失败,空间不足
 
  fclose(th);
 
  flock(fh, LOCK_UN);
 
  fclose(fh);
 
  unset(th, fh, in_dq, in_sq, in_lc, in_bc, i, j, k);
 
  return this->error_occur(0x000D, "");
 
  }
 
  tbuffer = "";
 
  }
 
  }
 
  unset(fbuffer, tbuffer, fstats, in_dq, in_sq, in_lc, in_bc);
 
  rewind(fh); // 回移文件指针到文件首
 
  rewind(th);
 
  i = j = "";
 
  k = 0;
 
  while (!feof(th)) { // 将临时文件数据写回源文件
 
  i = fgets(th, this->buffer_size);
 
  if (j=="") { // 获得文件系统的换行符
 
  j= substr(i, -2);
 
  if (j=="/r/n") k = 2;
 
  elseif (j[1]=="/r"||j[1]=="/n") {
 
  k = 1;
 
  j = j[1];
 
  } else j = "";
 
  }
 
  if (substr(i, -k)==j) {
 
  i = rtrim(substr(i, 0, -k), " /t");
 
  if (strlen(i)) fwrite(fh, i.j); // 清除右方空格
 
  else continue;
 
  } else fwrite(fh, rtrim(i, " /t"));
 
  }
 
  fflush(fh); // 保存、关闭文件
 
  ftruncate(fh, ftell(fh));
 
  fclose(th);
 
  flock(fh, LOCK_UN);
 
  fclose(fh);
 
  unset(i, j, k, fh, th);
 
  return TRUE;
 
  }
 
  }
 
  /**
 
  * @]Error List[=
 
  * 0x0001 指定目录不存在
 
  * 0x0002 指定目录无读取权限
 
  * 0x0003 指定目录无写入权限
 
  * 0x0004 指定目录无删除权限
 
  * 0x0005 指定文件不存在
 
  * 0x0006 指定文件无读取权限
 
  * 0x0007 指定文件无写入权限
 
  * 0x0008 指定文件无删除权限
 
  * 0x0009 指定文件无法锁定
 
  * 0x000A 指定对象不存在
 
  * 0x000B 方法指定参数不正确
 
  * 0x000C 无法创建临时文件
 
  * 0x000D 磁盘空间不足
 
  * 0x000E
 
  * 0x000F
 
  * 0x0010
 
  * 0x0011
 
  *
 
  */
 
  ?>.
 

(编辑:财气旺网 - 海宁网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!