檔案系統掃描是許多維護腳本的基礎需求。本文設計 FindFile 基礎遍歷類別與 UnsetBom 繼承實作,展示「模板方法模式」在檔案處理中的應用:基類負責遞迴遍歷與深度控制,子類專注具體檔案處理邏輯。
<?php
class FindFile {
public $files = []; // 收集處理過的檔案路徑
protected $maxdepth = 0; // 0 表示無深度限制
public function process($spath, $maxdepth = 0) {
if (is_numeric($maxdepth) && $maxdepth > 0) $this->maxdepth = $maxdepth;
else $this->maxdepth = 0;
$this->files = [];
$this->traversing($spath);
}
private function traversing($spath, $depth = 1) {
if ($handle = opendir($spath)) {
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') continue;
$curfile = $spath . '/' . $file;
if (is_dir($curfile)) {
if ($this->maxdepth == 0 || $depth < $this->maxdepth) {
$this->traversing($curfile, $depth + 1);
}
} else {
$this->handle($curfile); // 模板方法:由子類實作
}
}
closedir($handle);
}
}
protected function handle($file) {
array_push($this->files, $file); // 預設僅收集路徑
}
}
?>
<?php
class UnsetBom extends FindFile {
private $filetype = []; // 允許處理的副檔名
public function __construct($filetype = []) {
$this->filetype = $filetype;
}
protected function handle($file) {
if ($this->check_ext($file) && $this->check_utf8bom($file)) {
$this->clear_utf8bom($file);
array_push($this->files, $file); // 記錄已處理檔案
}
}
private function check_utf8bom($file) {
$content = file_get_contents($file);
return strlen($content) >= 3 &&
ord($content[0]) === 0xEF &&
ord($content[1]) === 0xBB &&
ord($content[2]) === 0xBF;
}
private function clear_utf8bom($file) {
$content = file_get_contents($file);
file_put_contents($file, substr($content, 3)); // 移除前 3 位元組
}
private function check_ext($file) {
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
return empty($this->filetype) || in_array($ext, $this->filetype);
}
}
?>
<?php
require 'FindFile.class.php';
require 'UnsetBom.class.php';
$folder = dirname(__FILE__);
$obj = new UnsetBom(['php', 'css', 'js', 'html']); // 指定副檔名
$obj->process($folder); // 掃描當前目錄(無深度限制)
print_r($obj->files); // 輸出已移除 BOM 的檔案清單
此結構採用模板方法模式:
FindFile::traversing() 定義遍歷骨架(遞迴、深度控制、目錄/檔案判斷)。FindFile::handle() 為抽象操作點(Hook),預設實作為收集路徑。UnsetBom 覆寫 handle() 注入具體業務邏輯(副檔名過濾、BOM 偵測、移除)。class CountLines extends FindFile {
protected $total = 0;
protected function handle($file) {
if ($this->check_ext($file)) {
$lines = count(file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
$this->total += $lines;
$this->files[$file] = $lines;
}
}
private function check_ext($file) {
return in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), ['php','js','css','html']);
}
public function getTotal() { return $this->total; }
}
class SearchContent extends FindFile {
private $keyword;
public function __construct($keyword, $filetype = []) {
$this->keyword = $keyword;
$this->filetype = $filetype;
}
protected function handle($file) {
if ($this->check_ext($file)) {
$content = file_get_contents($file);
if (stripos($content, $this->keyword) !== false) {
$this->files[] = $file;
}
}
}
private function check_ext($file) {
return empty($this->filetype) || in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), $this->filetype);
}
}
file_get_contents 會將整檔讀入記憶體,大檔案(>100MB)建議改用流式讀取 fopen/fgets。gc_collect_cycles() 或分批處理。/,PHP 內部自動轉換。這個類別體系展示了如何將「遍歷」與「處理」解耦,新增檔案處理需求時只需繼承 FindFile 並實作 handle(),符合開放封閉原則,極易維護擴充。