PHP文件系統(tǒng)基本設(shè)置類
發(fā)表時(shí)間:2024-06-11 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]<?php/* * * ===========Z================= * QQ:118824 * MSN:snakevil_@hotmail.com * HP:http://www.snakevil.com/ * ===========Z==============...
<?php
/*
*
* ===========Z=================
* QQ:118824
* MSN:snakevil_@hotmail.com
* HP:http://www.snakevil.com/
* ===========Z=================
*
*/
/**
* @]Class Name[= IO
* @]Class URI[= System.IO
* @]Purpose[=
* 本類用于對文件系統(tǒng)的處理
* @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
* @]Version[= 1.1.1
* @]Create[= 17:13 2004-3-25
* @]Modifications[=
* 4:04 2004-3-30
* + 修復(fù) generate_path() 方法中存在的一些 BUG
* + 重新設(shè)計(jì)方法 no_comment()
* 4:32 2004-3-29
* + 簡化方法 list_dir() 的返回值
* + 增加方法 file_info() 獲取文件或目錄信息
* 5:35 2004-3-28
* + 整理優(yōu)化算法
* 7:31 2004-3-27
* + 將錯(cuò)誤處理抽象為基類
* + 增加方法 no_comment() 刪除文件中 C 規(guī)范注釋
* @]See[=
*/
class IO extends SnkClass {
var $result; // 操作返回結(jié)果,如方法返回值為 mixed,則成功操作結(jié)果可在此獲得
var $exec_cmd; // 執(zhí)行方法,暫時(shí)沒應(yīng)用到
var $exist_dir; // 創(chuàng)建目錄時(shí)最后存在的目錄,現(xiàn)用于 copy() 和 move()
var $buffer_size; // 文件讀取緩沖區(qū)大小,根據(jù)服務(wù)應(yīng)用規(guī)模和服務(wù)器配置修改,建議默認(rèn)值
function IO() {
parent::SnkClass();
$this->result = array();
$this->exec_cmd = "";
$this->exist_dir = "";
$this->buffer_size = 8192;
return $this;
}
/**
* @]Method Name[= list_dir()
* @]Purpose[=
* 讀取指定目錄內(nèi)容,返回內(nèi)容數(shù)組
* @]Parameter[=
* string $dir_path 指定目錄路徑,默認(rèn)為當(dāng)前目錄
* @]Return[= mixed 錯(cuò)誤返回 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; // 忽略系統(tǒng)特定文件夾
$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(); // 清除文件系統(tǒng)緩存
return $this->result = $result;
}
/**
* @]Method Name[= file_info()
* @]Purpose[=
* 獲取指定文件或目錄的屬性
* @]Parameter[=
* string $dir_path 指定目錄路徑,默認(rèn)為當(dāng)前目錄
* @]Return[= mixed 錯(cuò)誤返回 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[=
* 根據(jù)正則表達(dá)式條件,在相應(yīng)目錄及給定層次的子目錄中搜索匹配的文件、目錄
* @]Parameter[=
* string $pattern 兼容 PERL 標(biāo)準(zhǔn)的正則表達(dá)式指明搜索匹配要求,會添加 /^ $/,默認(rèn)為 .*
* string $path 進(jìn)行搜索的目錄路徑,默認(rèn)為當(dāng)前路徑
* enum $seesk_type 有 -1 0 1 三種可能值,0 僅文件夾,1 僅文件,-1 兩者都包括,默認(rèn)為 1
* int $sub_dir 搜索的子目錄深度,指定目錄不算,建議不要超過 5,默認(rèn)為 0
* limit $limit 搜索結(jié)果限制,避免過度浪費(fèi)系統(tǒng)資源,默認(rèn)為 100
* @]Return[= mixed 錯(cuò)誤返回 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) {
/* 檢查參數(shù)值 */
$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; // 對于非目錄非文件項(xiàng)目,跳過
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); // 將下層目錄搜索結(jié)果添加
}
if ($seek_type+$i[$j]["type"]==1 !preg_match("/^".$pattern."$/", $i[$j]["name"])) continue; // 如果不搜索當(dāng)前類型,跳過
$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[=
* 刪除指定對象,文件或文件夾——包括內(nèi)含子目錄和文件的非空文件夾
* @]Parameter[=
* string $path 指定要?jiǎng)h除的內(nèi)容路徑,文件或目錄均可
* @]Return[= boolean 錯(cuò)誤返回 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; // 刪除目錄內(nèi)容出錯(cuò)
unset($i, $j, $k);
return TRUE;
}
}
/**
* @]Method Name[= generate_path()
* @]Purpose[=
* 獲取現(xiàn)有或不存在文件、目錄的絕對地址
* @]Parameter[=
* string $path 要獲取地址的文件、目錄現(xiàn)有相對、絕對地址
* @]Return[= string 獲得的地址
* @]Author[= SNakeVil <51JS,BU,PHPx> (snakevil@qq.com)
* @]See[=
*/
function generate_path($path="") {
$i = "/"==DIRECTORY_SEPARATOR ? "\\" : "/"; // 統(tǒng)一目錄分割符
$path = str_replace($i, DIRECTORY_SEPARATOR, strval($path));
if ($path[strlen($path)-1]!=DIRECTORY_SEPARATOR) $path .= DIRECTORY_SEPARATOR;
$i = strpos($path, DIRECTORY_SEPARATOR); // 獲得路徑中首個(gè)目錄分割符的位置
$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)) { // 對剩余內(nèi)容處理
$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 錯(cuò)誤返回 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 算法比較兩個(gè)文件是否相同
* @]Parameter[=
* string $src 源文件路徑
* string $dst 目標(biāo)文件路徑
* boolean $interal 對于超過 1MB 文件,設(shè)置 FALSE 省去 MD5 檢驗(yàn)步驟,減輕服務(wù)器負(fù)擔(dān)
* @]Return[= boolean 錯(cuò)誤返回 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 效驗(yàn)不符合,內(nèi)容不相同
return TRUE;
}
/**
* @]Method Name[= copy()
* @]Purpose[=
* 對任意文件夾、文件進(jìn)行復(fù)制,相對或絕對路徑皆可,文件復(fù)制完成后會進(jìn)行效驗(yàn),檢查是否出錯(cuò)數(shù)據(jù)錯(cuò)誤
* @]Parameter[=
* string $src_path 指定要復(fù)制的源內(nèi)容路徑,文件或目錄均可
* string $dst_path 指定要復(fù)制的目標(biāo)內(nèi)容路徑,文件或目錄均可,性質(zhì)由 $src_path 決定,可為 $src_path 下層目錄
* @]Return[= boolean 錯(cuò)誤返回 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)) { // 處理目錄
/*
* 關(guān)于算法的說明:
* 本來打算使用很簡單的遞歸算法,遇神殺神,遇魔斬魔的,后來發(fā)現(xiàn)一個(gè)問題:如果目標(biāo)路徑
* 為源路徑的后代路徑怎么辦?這樣算法會不停的探測下去…
* 于是添加了 $this->exist_dir 屬性,用來記錄這一情況下目標(biāo)路徑中存在的部分。于是新的問
* 題出來了:如何保存這一屬性?
* 將整個(gè)功能整合到 $this->copy() 方法中,那么必然需要在這個(gè)函數(shù)中記錄 $this->exist_dir
* 的變化,于是乎需要另外的一個(gè)有效的方法來阻止每一次操作中對其的更改。
* 作為變通,我使用的隱藏參數(shù) $sub,這個(gè)參數(shù)無論如何,只要算法不變,永遠(yuǎn)在參數(shù)表的最
* 后一個(gè)。因此,方法開始變得不穩(wěn)定,但這也沒有辦法,只能希望程序員自己不要故意破壞。
* 在外部調(diào)用時(shí),因?yàn)槟J(rèn) FALSE,所以對 $this->exist_dir 寫。內(nèi)部遞歸時(shí),顯性 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; // 源路徑為記錄的目標(biāo)路徑
if (TRUE!==$sub) $this->exist_dir = ""; // 記錄創(chuàng)建目錄前目標(biāo)目錄路徑中存在的目錄路徑
if (!$this->make_dir($dst)) return FALSE; // 創(chuàng)建目錄
if (FALSE===$i=$this->list_dir($src)) return FALSE; // 讀取目錄出錯(cuò)
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); // 復(fù)制文件失敗刪除新文件
return $this->error_occur(0x0007, $dst);
}
return TRUE;
}
}
/**
* @]Method Name[= move()
* @]Purpose[=
* 對任意文件夾、文件進(jìn)行移動,相對或絕對路徑皆可,文件移動完成后會進(jìn)行效驗(yàn),檢查是否出錯(cuò)數(shù)據(jù)錯(cuò)誤
* @]Parameter[=
* string $src_path 指定要移動的源內(nèi)容路徑,文件或目錄均可
* string $dst_path 指定要移動的目標(biāo)內(nèi)容路徑,文件或目錄均可,性質(zhì)由 $src_path 決定,可為 $src_path 下層目錄
* @]Return[= boolean 錯(cuò)誤返回 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); // 對非目標(biāo)目錄的上層目錄,刪除
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 規(guī)范的注釋
* @]Parameter[=
* string $path 指定要執(zhí)行操作的文件
* @]Return[= boolean 錯(cuò)誤返回 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); // 創(chuàng)建臨時(shí)文件
$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); // 文件讀取緩沖區(qū)
$tbuffer = ""; // 臨時(shí)文件緩沖區(qū)
$in_dq = $in_sq = $in_lc = $in_bc = FALSE;
while ($fblen=strlen($fbuffer)) { // 處理原始數(shù)據(jù)
$fstats = feof($fh);
for ($i=0;$i<$fblen;$i++) { // 分析文件內(nèi)容
if (!$fstats&&$i+5>$fblen) break; // 文件未完全讀取時(shí)臨近緩沖區(qū)讀取完成讀取下一塊文件內(nèi)容
$j = substr($fbuffer, $i, 2);
$k = $j[0];
if ($j=="/*"&&!$in_dq&&!$in_sq&&!$in_lc) { // 不在字符串和行注釋中,塊注釋開始
$in_bc = TRUE;
$i++;
} elseif ($j=="*/"&&$in_bc) { // 塊注釋結(jié)束
$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; // 行注釋結(jié)束
elseif ($j=="\\\\" $j=="\\\"" $j=="\\'") { // 轉(zhuǎn)義字符
$tbuffer .= $j;
$i++;
continue;
} elseif ($k=="\""&&!$in_sq&&!$in_bc&&!$in_lc) $in_dq = !$in_dq; // 雙引號字符串開始、結(jié)束
elseif ($k=="'"&&!$in_dq&&!$in_bc&&!$in_lc) $in_sq = !$in_sq; // 單引號字符串開始、結(jié)束
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) { // 寫入合法數(shù)據(jù)到臨時(shí)文件
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)) { // 將臨時(shí)文件數(shù)據(jù)寫回源文件
$i = fgets($th, $this->buffer_size);
if ($j=="") { // 獲得文件系統(tǒng)的換行符
$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); // 保存、關(guān)閉文件
ftruncate($fh, ftell($fh));
fclose($th);
flock($fh, LOCK_UN);
fclose($fh);
unset($i, $j, $k, $fh, $th);
return TRUE;
}
}
/**
* @]Error List[=
* 0x0001 指定目錄不存在
* 0x0002 指定目錄無讀取權(quán)限
* 0x0003 指定目錄無寫入權(quán)限
* 0x0004 指定目錄無刪除權(quán)限
* 0x0005 指定文件不存在
* 0x0006 指定文件無讀取權(quán)限
* 0x0007 指定文件無寫入權(quán)限
* 0x0008 指定文件無刪除權(quán)限
* 0x0009 指定文件無法鎖定
* 0x000A 指定對象不存在
* 0x000B 方法指定參數(shù)不正確
* 0x000C 無法創(chuàng)建臨時(shí)文件
* 0x000D 磁盤空間不足
* 0x000E
* 0x000F
* 0x0010
* 0x0011
*
*/
?>