phpでディレクトリツリーを辿りファイルを再帰的に処理する関数

phpでターゲットディレクトリ内のファイルを再帰的に処理する関数を作った。汎用的に使えるように関数やクラスでのコールバック呼び出しに一応対応しています。

昔perlでディレクトリを再帰的に辿りファイルをhogeするスクリプトを書いた記憶あるけど、見つからんので、phpで車輪の再発明。

glob()関数もあるけど特定のファイルやディレクトリの無視の仕方が分からん。。。いや、さっき存在、知ったんですけどね。。。(汗

特徴

  • 再帰的にディレクトリツリーを辿り中のファイルを全てhogeする関数です。
  • パターンが一致したもののみ処理します。
  • func callback($path,$args)のような形式のコールバック関数使えます。クラスメソッドにも対応。

検索パターンについて

  • *.txtのようなワイルドカード指定ができます。
  • パターンの先頭に!を付けることで除外パターンを指定できます。
  • パターンの最後に/を付けるとディレクトリです。
  • カンマ(,)連結で複数パターンを指定できます。
htmlファイルのみ処理
*.html

template_cディレクトリを除外
!template/

.で始まるディレクトリを除外
.*/

ソースコード

/**
 * $targetを再帰的に$patternに一致したファイル走査し、$callbackを実行する関数
 *
 * @param string $target 再帰して検索させるディレクトリ
 * @param string $pattern 調べるパターン。シェルワイルドカードで指定。ディレクトリは除外パターン(先頭に!)のみ有効。カンマ区切りで複数指定可
 * @param string $callback 検索されたファイルを処理する関数名
 * @param array $args $callbackに渡す引数(可変ではない)
 * @return void
 */
function fn_directory_recursion($target, $pattern, $callback='', $args=null)
{
	if (!is_dir($target)) return false;

	$target = add_last_slash($target); //ディレクトリは最後に/を付加

	$nodes = array();
	$dir = opendir($target);
	//ディレクトリ内のファイル名を1つずつを取得
	while( $node = readdir( $dir ) ){
		if ($node !== '.' && $node !== '..') {
			$nodes[] = $node; //ファイルリスト作成
		}
	}
	//ディレクトリ・ハンドルをクローズ
	closedir( $dir );

	//ファイルリストをチェック
	foreach ($nodes as $node) {
		$path = $target.$node;
		if (is_dir($path)) {
			//ディレクトリの場合、除外パターンだけ有効
			$node = add_last_slash($node); //ディレクトリは最後に/を付加
			if (is_pattern_match($pattern, $node) !== -1) { //除外以外の場合
				fn_directory_recursion($path, $pattern, $callback, $args);
			}
		} else if(is_file($path)) {
			//ディレクトリの場合、一致パターンだけ有効
			if (is_pattern_match($pattern, $node)) {
				if (is_array($callback)) {
					//正しいものとしてチェックを行わない。
					$instace = $callback[0];
					$method = $callback[1];
					$instace->$method($path,$args);
				} else {
					$callback($path,$args); //あるものとして存在チェックは行わない。
				}
			}
		}
	}
}

/**
 * $strの最後に/を付加する関数。$strの最後に/が初めから付いている場合は付加しない。
 *
 * @param string $str /を付加する文字列
 * @return string /を最後に付加した文字列
 */
function add_last_slash($str)
{
	if (!preg_match('/\/$/',$str)) {
		$str .= '/';
	}
	return $str;
}

/**
 * $strが$patternに一致していることを調べる関数
 *
 * @param string $pattern 調べるパターン。シェルワイルドカードで指定。ディレクトリは除外パターン(先頭に!)のみ有効。カンマ区切りで複数指定可
 * @param string $str 調べる文字列
 * @return int -1:除外パターン一致、1:パターン一致、0:一致パターン無し
 */
function is_pattern_match($pattern, $str)
{
	$pats = explode(',',$pattern);
	foreach ($pats as $pat) {
		//頭が!で始まるパターンは除外パターン
		if (preg_match('/^!(.*)/',$pat,$m)) {
			$pat = $m[1];
			if (fnmatch($pat, $str)) {
				return -1; //除外
			}
		} else {
			if (fnmatch($pat, $str)) {
				return 1; //一致
			}
		}
	}
	return 0;
}

実行コード

コールバック関数呼び出し

fn_directory_recursion(
	'/home/hogehoge/work',
	'!.*/,!templates_c/,temp/,*.htm,*.html,*.php,*.sql,*.txt',
	'callback_test',
	array('a','b')
);

function callback_test($path,$args)
{
	echo "test1-> $path $args[0] $args[1]\n";
}

コールバックメソッド(?)呼び出し

$ct = new CallbackTest();
$ct-> make_file_list();

class CallbackTest
{
	var $files = array();

	function __construct()
	{
	}

	function make_file_list()
	{
		$this->files = array();
		fn_directory_recursion(
			'/home/hogehoge/work',
			'!.*/,!templates_c/,temp/,*.htm,*.html,*.php,*.sql,*.txt',
			array(&$this,'push')
		);
		print_r($this->files);
	}

	function push($path,$args)
	{
		$this->files[] = $path;
	}
}

最後に

ブログに書いとけば車輪の再発明は防げる!というか探しやすい。

ページトップ

ブックマーク!


コメント投稿

* は必須項目です。入力したメールアドレスは公開されません。

*
*



AccessやExcel、.NETソフトウェア開発、WordPress等を使用したWebのシステム化、PCサポート、コンサルなどを行っています。全国対応可能です。お問い合わせください。
TEL (0776)56-8539